CPF em massa de teste e LGPD: dados fictícios
Como montar massa de teste com CPFs fictícios válidos, por que copiar produção é arriscado e a diferença entre anonimizar e mascarar.
Todo sistema que recebe CPF precisa de CPFs para ser testado: no formulário, na API, no banco, nos relatórios. O atalho mais comum é copiar uma parte da produção para a homologação. Ele é rápido, os dados parecem reais, e é justamente aí que mora o problema. Este guia mostra como montar uma massa de teste totalmente fictícia, como proteger o CPF quando ele precisa aparecer em telas e logs, e como escrever testes automatizados com números fixos.
CPF é dado pessoal
A Lei Geral de Proteção de Dados Pessoais (Lei 13.709/2018) define dado pessoal como informação relacionada a pessoa natural identificada ou identificável. O CPF é um identificador individual, emitido para uma única pessoa, então se encaixa sem esforço nessa definição. Ele não está na lista de dados pessoais sensíveis, como saúde ou origem racial, mas isso não reduz as obrigações gerais: tratar o CPF exige base legal, finalidade definida e cuidado com a segurança.
O ponto que costuma passar despercebido é que a LGPD não distingue ambiente. Um CPF real em uma base de homologação continua sendo um dado pessoal real, tratado pela mesma empresa, com as mesmas obrigações. A diferença é que esse ambiente normalmente tem controles mais fracos.
Riscos de copiar produção para homologação
Uma cópia de produção leva os dados reais para um lugar que não foi desenhado para protegê-los. Alguns riscos típicos:
- Mais gente com acesso. Desenvolvedores, testadores, terceiros e ferramentas de integração costumam ter acesso amplo à homologação, muitas vezes com a mesma credencial compartilhada.
- Menos controle. Backups sem criptografia, logs detalhados, dumps baixados para máquinas pessoais e bancos expostos por engano são mais comuns em ambientes de teste.
- Finalidade diferente. Os dados foram coletados para prestar um serviço ao cliente, não para testar software. Usá-los para outra finalidade exige avaliar se há base legal para isso.
- Efeitos colaterais reais. Um teste que dispara e-mails, SMS ou cobranças pode atingir os clientes de verdade se os contatos forem reais.
- Cópias que se multiplicam. Uma vez fora da produção, os dados são copiados de novo para outros ambientes, planilhas e anexos de chamados, e fica difícil saber onde estão.
A alternativa mais segura é não levar dado real nenhum: gerar a massa do zero.
Gerar massa fictícia válida
Um CPF fictício para teste precisa ser estruturalmente válido, ou o sistema vai recusá-lo antes de chegar ao trecho que você quer testar. O gerador de CPF resolve um número por vez; para volumes maiores, o gerador de CPF em lote produz até 10 mil números por vez, com escolha de estado e formato, e baixa o resultado em CSV ou JSON. A mesma geração está na API, em GET /api/v1/cpf/gerar?quantidade=1000&formatado=false, com até 1000 CPFs por chamada.
Para seeds que rodam em pipeline, é mais prático gerar no próprio código. O script abaixo cria um CSV de clientes fictícios, sem CPFs repetidos:
// uso: node gerar-massa.mjs 5000 > clientes.csv
import { randomInt } from 'node:crypto';
function calcularDigito(digitos, pesoInicial) {
let soma = 0;
for (let i = 0; i < digitos.length; i++) {
soma += Number(digitos[i]) * (pesoInicial - i);
}
const resto = soma % 11;
return resto < 2 ? 0 : 11 - resto;
}
function gerarCpf() {
let base;
do {
base = Array.from({ length: 9 }, () => randomInt(10)).join('');
} while (/^(\d)\1{8}$/.test(base)); // base repetida gera CPF repetido
const dv1 = calcularDigito(base, 10);
const dv2 = calcularDigito(base + dv1, 11);
return base + dv1 + dv2;
}
const quantidade = Number(process.argv[2] ?? 100);
const usados = new Set();
console.log('id,nome,email,cpf');
while (usados.size < quantidade) {
const cpf = gerarCpf();
if (usados.has(cpf)) continue;
usados.add(cpf);
const id = usados.size;
console.log([id, 'Cliente Teste ' + id, 'cliente' + id + '@example.com', cpf].join(','));
}Três detalhes do script valem para qualquer linguagem. A base com nove dígitos iguais é descartada, porque ela sempre resulta em uma sequência repetida como 111.111.111-11, que validadores recusam. O Set evita duplicatas, que quebrariam um índice único. E os e-mails usam example.com, domínio reservado para documentação, para que nenhum envio chegue a uma caixa real.
Bibliotecas de dados falsos, como o Faker com o locale pt_BR, também geram CPFs e nomes brasileiros. Versões do gerador em várias linguagens estão em JavaScript, Python e Java.
Lembre que um número gerado pode coincidir com um CPF real por acaso. Isso não é um problema dentro do ambiente de teste, mas é o motivo pelo qual esses números nunca devem ser usados em cadastros reais.
Anonimizar com máscara parcial
Às vezes o CPF real precisa aparecer: na tela do atendimento, em um log de auditoria, em um relatório. Nesses casos, a máscara parcial reduz a exposição. A função mascararCpf, usada pelas ferramentas deste site, mostra só os dígitos do meio:
function mascararCpf(valor) {
const d = String(valor ?? '').replace(/\D/g, '');
if (d.length !== 11) return '';
return '***.' + d.slice(3, 6) + '.' + d.slice(6, 9) + '-**';
}
mascararCpf('529.982.247-25'); // '***.982.247-**'É importante ter clareza sobre o que essa máscara é. Apesar do título desta seção, máscara parcial não é anonimização. A LGPD considera anonimizado o dado que perdeu a possibilidade de associação a uma pessoa, considerando os meios técnicos razoáveis disponíveis, e só esse dado deixa de ser dado pessoal. A máscara acima mantém seis dos onze dígitos. Como os verificadores são calculados a partir da base, sobram apenas três dígitos desconhecidos, ou seja, mil candidatos. Ao lado de um nome ou de um e-mail, a pessoa continua identificável.
A distinção que a lei faz é esta:
- Anonimização: o vínculo com a pessoa é desfeito de forma que não possa ser revertido com esforço razoável. Na prática, para massa de teste, isso significa substituir o CPF por um número gerado, sem guardar a correspondência, e fazer o mesmo com os demais campos identificadores.
- Pseudonimização: o dado só pode ser associado à pessoa com uma informação adicional mantida separadamente, como uma tabela de correspondência ou uma chave. O dado pseudonimizado continua sendo dado pessoal.
A máscara parcial está mais próxima de uma medida de minimização: mostra menos do que o dado completo, o que é útil em telas e logs. Um hash do CPF também não anonimiza; sem segredo, ele pode ser revertido testando todas as bases possíveis.
Fixtures em testes automatizados
Testes automatizados devem usar CPFs fixos, conhecidos e documentados, e não números sorteados a cada execução: um teste que falha só às vezes é difícil de investigar. Os números 529.982.247-25 e 111.444.777-35 são válidos e aparecem em documentação técnica, e 529.982.248-06 cobre o caso em que um verificador vem do resto 0. Para os casos inválidos, use 111.111.111-11 (sequência repetida) e 529.982.247-26 (segundo verificador errado).
Ajuste o import para a função de validação do seu projeto. Em Jest ou Vitest, a API de it.each é a mesma:
import { describe, it, expect } from 'vitest'; // no Jest, remova esta linha
import { validarCpf } from '../src/cpf';
export const CPFS_VALIDOS = ['529.982.247-25', '52998224725', '111.444.777-35', '529.982.248-06'];
export const CPFS_INVALIDOS = ['111.111.111-11', '529.982.247-26', '5299822472'];
export const clienteFicticio = (sobrescrever = {}) => ({
nome: 'Cliente Teste',
email: '[email protected]',
cpf: '52998224725',
...sobrescrever,
});
describe('validarCpf', () => {
it.each(CPFS_VALIDOS)('aceita %s', (cpf) => {
expect(validarCpf(cpf)).toBe(true);
});
it.each(CPFS_INVALIDOS)('recusa %s', (cpf) => {
expect(validarCpf(cpf)).toBe(false);
});
it('aceita o CPF do cliente fictício', () => {
expect(validarCpf(clienteFicticio().cpf)).toBe(true);
});
});Em Pytest, a fixture e a parametrização ficam assim:
import pytest
from app.cpf import validar_cpf
CPFS_VALIDOS = ["529.982.247-25", "52998224725", "111.444.777-35", "529.982.248-06"]
CPFS_INVALIDOS = ["111.111.111-11", "529.982.247-26", "5299822472"]
@pytest.fixture
def cliente_ficticio():
return {"nome": "Cliente Teste", "email": "[email protected]", "cpf": "52998224725"}
@pytest.mark.parametrize("cpf", CPFS_VALIDOS)
def test_aceita_cpf_valido(cpf):
assert validar_cpf(cpf) is True
@pytest.mark.parametrize("cpf", CPFS_INVALIDOS)
def test_recusa_cpf_invalido(cpf):
assert validar_cpf(cpf) is False
def test_cliente_ficticio_tem_cpf_valido(cliente_ficticio):
assert validar_cpf(cliente_ficticio["cpf"]) is TrueE em PHPUnit 10 ou mais recente, com provedores de dados declarados por atributo:
<?php
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
final class CpfTest extends TestCase
{
public static function cpfsValidos(): array
{
return [
'formatado' => ['529.982.247-25'],
'só dígitos' => ['52998224725'],
'outro válido' => ['111.444.777-35'],
'dv1 do resto 0' => ['529.982.248-06'],
];
}
public static function cpfsInvalidos(): array
{
return [
'repetido' => ['111.111.111-11'],
'dv2 errado' => ['529.982.247-26'],
'dez dígitos' => ['5299822472'],
];
}
#[DataProvider('cpfsValidos')]
public function testAceitaCpfValido(string $cpf): void
{
$this->assertTrue(validarCpf($cpf));
}
#[DataProvider('cpfsInvalidos')]
public function testRecusaCpfInvalido(string $cpf): void
{
$this->assertFalse(validarCpf($cpf));
}
}Centralizar os números em uma constante ou em um provedor de dados evita que CPFs apareçam espalhados pelos testes e facilita trocar um deles se for preciso. O guia sobre erros comuns ao validar CPF traz uma tabela com 12 casos para ampliar essas listas.
Checklist LGPD para ambientes de teste
Esta lista não substitui a orientação do encarregado de dados da sua empresa, mas cobre os pontos que mais aparecem na prática:
- A base de homologação é criada a partir de seeds fictícios, não de cópia da produção.
- Se houver cópia de produção, existe justificativa documentada, e CPF, nome, e-mail, telefone e endereço são substituídos por valores gerados antes de os dados saírem da produção.
- A substituição não guarda tabela de correspondência com os dados originais.
- Testes automatizados usam CPFs fixos de documentação ou gerados, nunca de pessoas reais, incluindo os da equipe.
- Serviços externos de e-mail, SMS, pagamento e consulta cadastral estão simulados ou apontam para ambientes de teste.
- Logs e telas de suporte exibem o CPF mascarado.
- Dumps e backups de teste têm acesso restrito e prazo para serem apagados.
- Capturas de tela e anexos de chamados não contêm dados reais.
- O acesso à homologação é individual, sem credenciais compartilhadas.
Nenhum item da lista exige ferramenta cara. A maior parte é decisão de processo, tomada uma vez e automatizada no pipeline.
Perguntas frequentes
Usar CPFs gerados nos testes resolve a questão da LGPD?
Resolve a parte do CPF, desde que o resto do registro também seja fictício. Um CPF gerado ao lado do nome, do e-mail e do endereço reais de um cliente continua compondo um dado pessoal. Os números gerados também podem coincidir com um CPF real por acaso, então não devem sair do ambiente de teste.
Trocar o CPF por um hash é anonimização?
Não. Um hash sem segredo pode ser revertido por força bruta, porque existem só cerca de um bilhão de bases de nove dígitos. Mesmo com segredo, o dado continua associável por quem tem a chave, o que caracteriza pseudonimização, e não anonimização.
Posso usar o meu próprio CPF nos testes?
Não é uma boa prática. O seu CPF passaria a circular em bancos de teste, logs e capturas de tela que muita gente acessa. Use os números fixos de documentação ou números gerados.