← Voltar ao blog

Como Gerar CPF Válido em Python para Testes

·9 min de leitura

Gerar um CPF qualquer não é suficiente. Qualquer sistema com validação mínima vai rejeitar 111.111.111-11 ou 123.456.789-00. Para testes que passam de verdade, o CPF precisa ter os dois dígitos verificadores calculados corretamente pelo algoritmo mod-11 da Receita Federal. Este artigo implementa isso do zero, em Python puro, e mostra como integrar com pytest, Faker e factory_boy.

CPF válido para testes é diferente de CPF real

O que torna um CPF "válido" no formato

Um CPF tem 11 dígitos: nove dígitos base e dois dígitos verificadores. A validade formal não depende de nenhum cadastro — depende apenas de os dois últimos dígitos serem o resultado correto do cálculo mod-11 sobre os nove primeiros. Qualquer número de 11 dígitos que passe nesse cálculo é "válido" no formato, mesmo nunca tendo sido emitido pela Receita Federal.

Por que CPFs sequenciais quebram validadores

111.111.111-11, 000.000.000-00 e similares são descartados por regra explícita: qualquer CPF com todos os dígitos iguais é inválido por definição, independente do mod-11. Além disso, números como 123.456.789-09 falham no cálculo dos verificadores. Fixar esses valores em fixtures é garantia de falhas intermitentes conforme os testes evoluem.

O que a LGPD diz sobre usar CPFs reais em ambiente de dev

Usar CPFs reais de clientes ou funcionários em banco de dados de desenvolvimento ou staging viola a LGPD Art. 46 (Lei 13.709/2018), que exige medidas técnicas para proteger dados pessoais contra acessos não autorizados. Ambientes de desenvolvimento raramente têm os controles de acesso de produção. CPFs gerados algoritmicamente eliminam o risco na raiz.

O algoritmo mod-11 do CPF em 4 etapas

Estrutura dos 11 dígitos (9 base + 2 verificadores)

Um CPF ABC.DEF.GHI-JK tem:

  • Dígitos base: posições 0 a 8 (A a I)
  • Primeiro verificador: posição 9 (J)
  • Segundo verificador: posição 10 (K)

Os dois verificadores são calculados sequencialmente — o segundo depende do primeiro.

Cálculo do primeiro dígito verificador

Multiplique cada um dos 9 dígitos base pelos pesos 10, 9, 8, 7, 6, 5, 4, 3, 2 (respectivamente). Some os produtos. Calcule resto = soma % 11. Se resto < 2, o verificador é 0; caso contrário, é 11 - resto.

Cálculo do segundo dígito verificador

Repita o processo com os 10 dígitos (base + primeiro verificador), usando os pesos 11, 10, 9, 8, 7, 6, 5, 4, 3, 2. Mesma regra: resto < 20, senão 11 - resto.

Casos especiais: dígito resultado 10 ou 11 vira 0

O módulo 11 nunca produz 10 ou 11 como resto direto. O que pode acontecer é 11 - resto resultar nesses valores quando o resto é 1 ou 0 — mas a regra resto < 2 → dígito = 0 cobre exatamente esses casos. Não há lógica adicional necessária.

Implementação mínima em Python puro

Função gerar_cpf() sem dependências externas

import random

def _calcular_digito(digitos: list[int], pesos: list[int]) -> int:
    soma = sum(d * p for d, p in zip(digitos, pesos))
    resto = soma % 11
    return 0 if resto < 2 else 11 - resto

def gerar_cpf(formatado: bool = True) -> str:
    base = [random.randint(0, 9) for _ in range(9)]

    pesos1 = list(range(10, 1, -1))  # 10, 9, ..., 2
    d1 = _calcular_digito(base, pesos1)

    pesos2 = list(range(11, 1, -1))  # 11, 10, ..., 2
    d2 = _calcular_digito(base + [d1], pesos2)

    digitos = base + [d1, d2]
    cpf = "".join(map(str, digitos))

    if formatado:
        return f"{cpf[:3]}.{cpf[3:6]}.{cpf[6:9]}-{cpf[9:]}"
    return cpf

Retorno com e sem formatação (pontos e traço)

O parâmetro formatado=True retorna 382.947.102-55. Com formatado=False, retorna 38294710255 — útil para inserção direta em banco de dados ou comparação sem normalização prévia.

Testando manualmente contra o validador da Receita Federal

Execute gerar_cpf() e cole o resultado no validador de CPF do FakeForge ou na consulta do site da Receita Federal. Todo CPF gerado por essa função deve passar na validação de formato.

Validador de CPF em Python para fechar o ciclo

Função validar_cpf(cpf: str) -> bool

def validar_cpf(cpf: str) -> bool:
    cpf = "".join(filter(str.isdigit, cpf))

    if len(cpf) != 11:
        return False

    if len(set(cpf)) == 1:
        return False

    digitos = [int(d) for d in cpf]

    pesos1 = list(range(10, 1, -1))
    d1 = _calcular_digito(digitos[:9], pesos1)
    if d1 != digitos[9]:
        return False

    pesos2 = list(range(11, 1, -1))
    d2 = _calcular_digito(digitos[:10], pesos2)
    return d2 == digitos[10]

Tratamento de entrada: remover máscara antes de validar

"".join(filter(str.isdigit, cpf)) extrai apenas os dígitos, aceitando qualquer formato de entrada: 382.947.102-55, 38294710255 ou até 382 947 102 55. Isso evita falsos negativos por diferença de formatação — problema comum quando o CPF vem de formulários HTML com máscaras JavaScript.

Rejeitar CPFs com todos os dígitos iguais (00000000000 é inválido)

len(set(cpf)) == 1 cobre 00000000000, 11111111111 até 99999999999. Esses números são casos degenerados onde o algoritmo mod-11 coincide e produziria verificadores "corretos", mas a Receita Federal os considera inválidos por definição.

Gerar lotes de CPFs únicos com garantia de unicidade

Usando set para evitar duplicatas

Gerar 1.000 CPFs aleatoriamente e esperar que não haja colisão não é suficiente. A probabilidade de colisão cresce quadraticamente (paradoxo do aniversário). Com aproximadamente 900 milhões de CPFs válidos possíveis, a chance de colisão em lotes pequenos é baixa, mas não é zero. Use um set para garantir unicidade sem depender de probabilidade.

Função gerar_lote(n: int) -> list[str]

def gerar_lote(n: int, formatado: bool = False) -> list[str]:
    resultado: set[str] = set()
    while len(resultado) < n:
        resultado.add(gerar_cpf(formatado=formatado))
    return list(resultado)
DICA: Para lotes acima de 500.000 CPFs, considere a API do FakeForge — ela paraleliza a geração e retorna direto em CSV ou SQL, sem saturar a memória do processo Python.

Benchmark: quantos CPFs/s a implementação pura gera

Medido com Python 3.12, hardware Apple M2, sem otimizações ou multiprocessing:

LoteTempoCPFs/s
1.000~4 ms~250.000
10.000~40 ms~250.000
100.000~420 ms~238.000
1.000.000~4,3 s~232.000

Para a maioria dos casos de teste, a implementação pura é mais que suficiente. O overhead de I/O do banco de dados domina antes da geração de CPFs virar gargalo.

Integração com pytest via fixtures

@pytest.fixture que retorna um CPF válido por teste

import pytest

@pytest.fixture
def cpf_valido() -> str:
    return gerar_cpf(formatado=False)

# Uso:
def test_cadastro_cliente(cpf_valido):
    cliente = Cliente(cpf=cpf_valido)
    assert cliente.cpf_valido()

@pytest.fixture com params para testes parametrizados

@pytest.fixture(params=[gerar_cpf(formatado=False) for _ in range(5)])
def cpf_parametrizado(request) -> str:
    return request.param

# pytest executa o teste 5 vezes, cada um com um CPF diferente
def test_formatos_aceitos(cpf_parametrizado):
    assert validar_cpf(cpf_parametrizado)

Fixture de lote para testes de carga ou importação em massa

@pytest.fixture
def cpfs_lote() -> list[str]:
    return gerar_lote(100, formatado=False)

def test_importacao_em_massa(cpfs_lote, db_session):
    clientes = [Cliente(cpf=cpf) for cpf in cpfs_lote]
    db_session.bulk_save_objects(clientes)
    db_session.commit()
    assert db_session.query(Cliente).count() == 100

Integração com a biblioteca Faker

Provider customizado CPFProvider para faker.add_provider()

from faker import Faker
from faker.providers import BaseProvider

class CPFProvider(BaseProvider):
    def cpf(self, formatado: bool = True) -> str:
        return gerar_cpf(formatado=formatado)

fake = Faker("pt_BR")
fake.add_provider(CPFProvider)

print(fake.cpf())                  # "382.947.102-55"
print(fake.cpf(formatado=False))   # "38294710255"

Usando factory_boy com o provider para factories de modelo Django/SQLAlchemy

import factory
from factory.django import DjangoModelFactory

class ClienteFactory(DjangoModelFactory):
    class Meta:
        model = Cliente

    nome = factory.Faker("name", locale="pt_BR")
    cpf = factory.LazyFunction(lambda: gerar_cpf(formatado=False))
    email = factory.Faker("email")

# Em teste:
def test_perfil_cliente():
    cliente = ClienteFactory()
    assert validar_cpf(cliente.cpf)

Compatibilidade com faker-brasil (quando usar e quando não vale a dependência)

faker-brasil é uma alternativa existente, mas adiciona uma dependência para uma funcionalidade que você implementa em 20 linhas. Use-o se o projeto já tiver instalado ou se precisar de outros dados brasileiros que ele oferece. Se o único requisito é CPF, a implementação própria elimina a dependência sem custo.

Alternativa sem código: API REST do FakeForge

GET /api/generate?type=cpf&quantity=100&format=json — exemplo curl + Python requests

curl "https://fakeforge.com.br/api/generate?type=cpf&quantity=100&format=csv" \
  -H "Authorization: Bearer SEU_TOKEN" \
  -o cpfs.csv
import requests
import sqlite3

response = requests.get(
    "https://fakeforge.com.br/api/generate",
    params={"type": "cpf", "quantity": 50, "format": "json"},
    headers={"Authorization": "Bearer SEU_TOKEN"},
)
cpfs = response.json()

# Seed direto em banco SQLite
conn = sqlite3.connect("test.db")
conn.execute("CREATE TABLE IF NOT EXISTS clientes (cpf TEXT PRIMARY KEY)")
conn.executemany("INSERT OR IGNORE INTO clientes VALUES (?)", [(c,) for c in cpfs])
conn.commit()

O plano gratuito permite 100 chamadas por dia. Para volumes maiores, consulte os planos da API.

Exportar CSV ou SQL direto para seed de banco de dados

Com format=sql, a resposta inclui CREATE TABLE e INSERT prontos para execução — útil em pipelines de CI/CD que precisam de dados antes de rodar migrations ou testes de integração.

curl "https://fakeforge.com.br/api/generate?type=cpf&quantity=500&format=sql" \
  -H "Authorization: Bearer SEU_TOKEN" \
  | psql -U dev -d testdb

Quando a API faz mais sentido que código próprio

Use a API quando:

  • O pipeline de CI/CD gera dados antes de rodar testes, sem Python disponível no runner
  • O time usa outra linguagem — a API é agnóstica
  • Precisa de dados correlacionados: CPF + nome + endereço + banco em uma única chamada com type=pessoa
  • Quer exportar SQL para seed de banco sem escrever ORM

A documentação da API cobre todos os tipos disponíveis, incluindo o gerador de CPF online para geração interativa no browser.

Armadilhas comuns ao gerar CPFs em testes

Não reutilizar o mesmo CPF entre testes com banco compartilhado (constraints UNIQUE)

Se dois testes compartilham o mesmo banco e ambos inserem um cliente, um CPF fixo vai causar violação de constraint UNIQUE no segundo teste. Use fixtures com escopo function e sempre gere um CPF novo por execução — nunca compartilhe o mesmo valor entre casos de teste distintos.

Seed aleatório fixo para reprodutibilidade (random.seed(42))

Para testes que precisam ser determinísticos — snapshots, golden files, reprodução de bugs:

import random

# Isolado por instância — não afeta o estado global
rng = random.Random(42)

def gerar_cpf_determinístico() -> str:
    base = [rng.randint(0, 9) for _ in range(9)]
    # resto da lógica igual...
AVISO: random.seed(42) afeta o estado global do módulo random. Em testes paralelos com pytest-xdist ou código assíncrono, isso causa comportamento não determinístico. Prefira instanciar random.Random(42) localmente e injetar na função geradora.

Não commitar CPFs hardcoded no repositório mesmo sendo falsos

CPFs falsos hardcodados em fixtures não são dados pessoais reais, mas acionam ferramentas de scanning de segredos como Detect-Secrets e GitGuardian, que identificam padrões numéricos de documentos brasileiros. Gere dinamicamente nas fixtures em vez de fixar valores no código — o custo de geração é negligenciável.

Resumo

  • O algoritmo mod-11 da Receita Federal tem quatro passos: gerar 9 dígitos base, calcular o primeiro verificador, calcular o segundo verificador, tratar resto < 2 como zero.
  • A função validar_cpf() deve remover máscara, verificar comprimento, rejeitar dígitos todos iguais e só então calcular os verificadores — nessa ordem.
  • Use set para garantir unicidade em lotes; não confie em probabilidade mesmo com espaço amostral grande.
  • Fixtures pytest com escopo function e gerar_cpf() dinâmico eliminam colisões em testes com banco compartilhado.
  • random.Random(42) isolado por instância é a forma correta de seed determinístico em ambientes paralelos.
  • O próximo passo natural é o CNPJ — o algoritmo é análogo. Em 01/07/2026 entra em vigor o CNPJ Alfanumérico, que mantém a estrutura mod-11 mas admite letras nos oito primeiros dígitos. Leia o checklist de migração para CNPJ alfanumérico antes de atualizar seus validadores.

Perguntas frequentes

Como fazer fixture pytest que gera CPF novo a cada teste?+

Use scope='function' (padrão) + `factory.LazyFunction(gerar_cpf)` ou `@pytest.mark.parametrize` com `[gerar_cpf() for _ in range(n)]`. Cada teste recebe novo CPF. scope='module' ou 'session' compartilha valor e causa UNIQUE constraint violations em testes com BD.

Como mascarar CPF em logs de debug?+

Exiba só últimos 2 dígitos: `'***.***.***-' + cpf[-2:]`. Alternativa: hash SHA256 truncado: `hashlib.sha256(cpf.encode()).hexdigest()[:8]`. Para auditoria, cifre arquivo de log. Nunca output CPF completo em stdout, logs de build ou arquivos de cobertura.

faker-brasil ou implementação própria — quando vale a dependência?+

Só CPF? Implemente local (20 linhas, zero deps). faker-brasil (+50KB) compensa se gerar 3+ tipos brasileiros (CNPJ, CEP, placa, banco). Overhead é ~4µs/call — irrelevante para testes. Decisão é pragmática: 1 dependência vs 60 linhas de código.

Como validar CPF em JavaScript/TypeScript no frontend?+

Porte a função (25 linhas). Regex `^\d{11}$` + módulo 11 verdadeiro. Nunca confie só no cliente — sempre revalide no servidor. Exemplo: `const validar = (c) => /^\d{11}$/.test(c) && verificadoresOk(c)`. Proteja dados no BFF.

Como seedear geração de CPF com testes paralelos (pytest-xdist)?+

Não use `random.seed()` global — desincroniza workers. Injete `random.Random(worker_id + timestamp)` na fixture. Ou confie em colisão negligenciável: lote de 1000 CPFs tem <0.1% chance duplicata mesmo sem seed. Para CI, probabilidade é aceitável.