Quase todo sistema brasileiro tem um formulário que pede CPF. E quase toda suíte de teste desse sistema tem, em algum canto, um "123.456.789-09" fixo no código.
Funciona por um tempo. Depois aparecem três problemas, quase sempre nesta ordem.
1. O CPF repetido
123.456.789-09 passa no dígito verificador. O problema começa quando o segundo teste cadastra outro usuário com o mesmo CPF e o banco devolve duplicate key value violates unique constraint. A saída de sempre é uma lista de CPFs "que funcionam" num arquivo compartilhado, que ninguém sabe de onde veio e que acaba no meio dos dados de produção.
2. O número com cara de CPF que não é CPF
A segunda tentativa costuma ser gerar onze dígitos aleatórios e aplicar a máscara. O formato fica certo, mas o validador do back-end rejeita: só 1 em cada 100 sequências aleatórias acerta os dois dígitos verificadores.
Gerar um CPF válido é curto. Os dois últimos dígitos saem de uma soma ponderada módulo 11:
function gerarCPF(): string { const n = Array.from({ length: 9 }, () => Math.floor(Math.random() * 10)); // 111.111.111-11 e afins passam na conta, mas são recusados if (n.every((d) => d === n[0])) return gerarCPF(); const digito = (base: number[]) => { const peso = base.length + 1; const soma = base.reduce((acc, d, i) => acc + d * (peso - i), 0); const resto = soma % 11; return resto < 2 ? 0 : 11 - resto; }; n.push(digito(n)); // pesos 10 a 2 n.push(digito(n)); // pesos 11 a 2 return n.join("").replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4"); } Se o seu teste só precisa de CPF, pode parar aqui: copie a função e siga em frente.
3. Os campos que não batem entre si
O terceiro problema aparece quando o sistema começa a cruzar dados. Antifraude, KYC e checkout com cartão olham se as peças fazem sentido juntas:
- o DDD do celular é de uma cidade e o CEP é de outro estado;
- o e-mail não tem nada a ver com o nome;
- o cartão está no nome de outra pessoa;
- a chave Pix não é o CPF, o e-mail nem o celular do cliente.
Cada campo isolado é válido, mas o cadastro como um todo é estranho, e o teste falha em lugar que não deveria. Ou pior: passa, porque o fixture nunca chegou perto do caso real.
Daí para frente a função de CPF vira um projeto: CNPJ (e o CNPJ alfanumérico, que a Receita começou a emitir em julho de 2026), cartão com Luhn, Pix nos quatro formatos do BACEN, DDD por cidade, banco com código COMPE de verdade.
O que eu acabei construindo
Passei por esses três problemas e montei o FakeForge, um gerador de dados brasileiros para teste com API. Aviso logo que o projeto é meu, então leve isso em conta. A API responde sem cadastro:
curl -s "https://fakeforge.com.br/api/generate?type=cpf&quantity=3" { "type": "cpf", "quantity": 3, "data": ["286.908.784-59", "814.125.695-59", "016.709.862-41"] } Para o problema 3, há os presets: cada item é um cliente inteiro, com os campos coerentes entre si.
curl -s "https://fakeforge.com.br/api/generate?preset=customer&quantity=1" { "nome": "Vinícius Campos Cunha", "cpf": "236.726.684-00", "email": "vinicius_campos694@hotmail.com", "telefone": "(85) 96986-1744", "endereco": { "cep": "60034-397", "street": "Avenida Presidente Kennedy, 1194", "neighborhood": "Dionísio Torres", "city": "Fortaleza", "state": "Ceará", "stateCode": "CE" } } DDD 85, CEP de Fortaleza e e-mail derivado do nome. O preset fintech vai além: as chaves Pix são o CPF, o e-mail e o celular do próprio cliente, e o cartão sai no nome dele.
Também dá para pedir em SQL ou CSV, com &format=sql ou &format=csv. O SQL já vem com o CREATE TABLE e o endereço achatado em colunas (endereco_cep, endereco_city...).
Nos testes
Existem SDKs para Node e Python, os dois sem dependência (npm install fakeforge-br e pip install fakeforge-br). A chave de API é lida da variável FAKEFORGE_API_KEY; sem ela, o SDK funciona no modo sem cadastro.
Playwright
import { test, expect } from "@playwright/test"; import { FakeForge } from "fakeforge-br"; const ff = new FakeForge(); test("cadastro com cliente novo a cada execução", async ({ page }) => { const [cliente] = await ff.customer(1); await page.goto("/cadastro"); await page.fill("#nome", cliente.nome); await page.fill("#cpf", cliente.cpf); await page.fill("#email", cliente.email); await page.fill("#telefone", cliente.telefone); await page.fill("#cep", cliente.endereco.cep); await page.click("button[type=submit]"); await expect(page.getByText("Conta criada")).toBeVisible(); }); Um cliente novo a cada execução acaba com o erro de CPF repetido sem precisar limpar o banco entre os testes.
pytest
# conftest.py import pytest from fakeforge import FakeForge ff = FakeForge() @pytest.fixture def cliente(): [c] = ff.customer(1) return c # test_cadastro.py ("api" é o cliente HTTP do seu projeto) def test_cadastro_aceita_cpf(api, cliente): resposta = api.post("/clientes", json=cliente) assert resposta.status_code == 201 Seed do banco no GitHub Actions
- name: Seed com 100 clientes env: FAKEFORGE_API_KEY: ${{ secrets.FAKEFORGE_API_KEY }} run: | curl -s -H "X-API-Key: $FAKEFORGE_API_KEY" \ "https://fakeforge.com.br/api/generate?preset=customer&quantity=100&format=sql" \ > seed.sql psql "$DATABASE_URL" -f seed.sql A autenticação é pelo cabeçalho X-API-Key, não por Authorization: Bearer.
Limites
Prefiro deixar os limites claros antes que alguém descubra no meio do pipeline:
- Sem chave: 50 chamadas por dia por IP e até 50 itens por chamada.
- Com chave grátis: as mesmas 50 chamadas por dia, com até 100 itens por chamada.
- Presets (clientes, pedidos): sem chave ou no plano grátis vem uma amostra de 3. O seed de 100 clientes acima precisa do plano pago.
Para testar um formulário ou rodar uma suíte pequena, o grátis sobra. Para seed grande ou CI rodando o dia todo, aí entram os planos pagos.
Qual é a sua?
O que mais quebra nos seus testes com dado brasileiro? Me conta nos comentários. Estou decidindo o que vem depois (inscrição estadual, RG por estado, CNH, boleto) e prefiro decidir com base em quem usa.
