← Voltar ao blog

LGPD em testes de software: guia prático para devs

·14 min de leitura

Usar dados reais de clientes em ambiente de teste é mais que descuido de segurança, é uma violação direta da Lei Geral de Proteção de Dados (Lei 13.709/2018). Este guia mostra como estruturar fixtures, seeds e pipelines de CI/CD sem tocar em dados pessoais reais, mantendo conformidade com a LGPD e reprodutibilidade nos testes.

---

Usar dados reais em testes viola a LGPD

O que a lei diz sobre finalidade e necessidade (Art. 6º, I e III)

A LGPD estabelece princípios que se aplicam a qualquer tratamento de dado pessoal, inclusive dentro da sua empresa. O Art. 6º, I define o princípio da finalidade: o tratamento só pode ocorrer para propósitos legítimos, específicos e informados ao titular. O Art. 6º, III define o princípio da necessidade: o tratamento deve se limitar ao mínimo indispensável para atingir sua finalidade.

Quando um titular de dados cedeu o CPF para contratar um serviço, a finalidade declarada não incluiu "ser usado como fixture em testes unitários". O uso nesse contexto viola o Art. 6º, I, independente de qualquer controle de acesso interno.

Por que "ambiente interno" não é base legal suficiente

Equipes argumentam que banco de testes é "ambiente controlado, sem saída para fora". A lei não faz essa distinção. O Art. 5º, X define tratamento como "toda operação realizada com dados pessoais", incluindo "armazenamento, utilização, acesso". Um banco de staging com dump de produção é tratamento de dados pessoais, ponto.

Além disso, "ambiente interno" expande o círculo de acesso. Estagiários, freelancers contratados, pipelines de CI com secrets vazados, snapshots de banco em logs de debug, todos esses vetores multiplicam o risco sem qualquer base legal que os cubra.

Incidentes reais: o caso dos dumps de produção compartilhados por e-mail

Um padrão recorrente em times de produto: o dev precisa de dados para reproduzir um bug de produção, pede ao DBA um "dump pequeno" e recebe um CSV com 50 mil linhas por e-mail. O arquivo fica na caixa de entrada, no laptop, eventualmente no repositório como fixtures/users_sample.csv.

A ANPD já publicou Resolução CD/ANPD nº 2/2022 sobre comunicação de incidentes. Dump de produção exposto em repositório Git se enquadra como incidente de segurança com notificação obrigatória em até 72 horas quando o acesso não autorizado é confirmado.

---

As bases legais que importam para dados de teste (LGPD Art. 7º)

Consentimento (Art. 7º, I) é inviável para fixtures

Consentimento exige que o titular autorize de forma livre, informada e inequívoca o tratamento para finalidade específica. Nenhuma empresa vai contatar cada titular cujo CPF aparece em um fixture de teste para pedir autorização. Operacionalmente inviável. Base legal descartada.

Legítimo interesse (Art. 7º, IX) e seus limites

Legítimo interesse permite tratar dados quando o controlador tem interesse legítimo, desde que não prevaleçam os direitos fundamentais do titular. Para dados de teste, a justificativa seria "garantir qualidade do software". O problema: a ANPD exige que o controlador demonstre que o tratamento é necessário e proporcional. Dados sintéticos atingem o mesmo objetivo sem tratar dados reais. Logo, o interesse legítimo não sobrevive ao teste de proporcionalidade do Art. 7º, IX.

Anonimização como saída: quando o dado sai do escopo da lei (Art. 5º, III)

A melhor saída legal não é uma base legal: é remover o dado do escopo da lei. O Art. 5º, III define dado anonimizado como "dado relativo a titular que não possa ser identificado, considerando a utilização de meios técnicos razoáveis e disponíveis na ocasião do tratamento". Dado anonimizado de forma irreversível não é dado pessoal. Não existe tratamento a justificar.

---

Diferença entre anonimização e pseudonimização na prática

Pseudonimização (Art. 13, §4º): ainda é dado pessoal

Pseudonimização substitui o identificador direto por um pseudônimo. Trocar o CPF 123.456.789-09 por um hash SHA-256 é pseudonimização. Se a tabela de correspondência existe em algum lugar, o dado é reversível e continua sendo dado pessoal sob a LGPD Art. 13, §4º.

Anonimização irreversível: critérios do ANPD

A ANPD publicou o Guia Orientativo de Anonimização com três critérios principais: não-individualização, não-vinculação e não-inferência. Um CPF gerado sinteticamente que nunca existiu não falha nenhum dos três critérios. É, por definição, anônimo desde a origem.

Qual técnica usar em cada camada (banco, log, API mock)

CamadaTécnica recomendadaTécnica a evitar
Banco de dados de testeGeração sintética desde o seedDump mascarado com hash reversível
Logs de aplicaçãoSubstituição por placeholder estáticoTruncagem parcial (ex: 123.XXX.XXX-09)
API mock / WireMockFixture com dados geradosCaptura de tráfego de produção
Snapshots de banco (CI)Gerados em build timeCópia automatizada de staging

---

Estratégias para construir fixtures sem dados reais

Geração sintética: dados correlacionados

O maior problema de dados aleatórios mal gerados não é a LGPD, é que quebram as próprias validações do sistema. Um CPF com dígitos verificadores errados vai falhar na camada de negócio antes de chegar ao banco. Dados sintéticos úteis precisam ser internamente consistentes: CPF válido, endereço no mesmo estado que o CEP, DDD de celular compatível com a cidade.

// Geração de fixture completa via FakeForge API
async function gerarPessoa() {
  const res = await fetch(
    "https://fakeforge.com.br/api/generate?type=pessoa&quantity=1&format=json"
  );
  const { data } = await res.json();
  return data[0] as {
    nome: string;
    cpf: string;
    email: string;
    telefone: string;
    endereco: {
      logradouro: string;
      bairro: string;
      cidade: string;
      estado: string;
      cep: string;
    };
  };
}

A API retorna nome, CPF com dígito verificador correto e endereço correlacionado ao mesmo estado. Detalhes em /docs.

Mascaramento de produção: regras por campo e riscos residuais

Quando o time insiste em partir de dados reais, mascaramento é o caminho. A regra básica: substituir o campo inteiro por dado sintético válido, nunca truncar ou embaralhar parcialmente.

// Substituir CPF real por CPF sintético em dump (TypeScript)
import { execSync } from "child_process";

function mascaraCpf(linha: string): string {
  // CPF no formato 000.000.000-00
  return linha.replace(
    /\b\d{3}\.\d{3}\.\d{3}-\d{2}\b/g,
    () => gerarCpfSintetico()
  );
}

function gerarCpfSintetico(): string {
  // Gera 9 dígitos base
  const base = Array.from({ length: 9 }, () => Math.floor(Math.random() * 10));

  const d1 = calcDigito(base, 10);
  const d2 = calcDigito([...base, d1], 11);

  const digits = [...base, d1, d2];
  return `${digits.slice(0, 3).join("")}.${digits.slice(3, 6).join("")}.${digits.slice(6, 9).join("")}-${digits.slice(9).join("")}`;
}

function calcDigito(nums: number[], peso: number): number {
  const soma = nums.reduce((acc, n, i) => acc + n * (peso - i), 0);
  const resto = soma % 11;
  return resto < 2 ? 0 : 11 - resto;
}
AVISO: mascaramento de dump tem risco residual de reidentificação por combinação de campos (nome + CEP + data de nascimento). Prefira geração sintética pura sempre que o caso de uso permitir.

Seed determinístico: reprodutibilidade sem dados reais

// Seed factory para Jest/Vitest
import { beforeAll } from "vitest";

let fixtures: Awaited<ReturnType<typeof gerarPessoa>>[] = [];

beforeAll(async () => {
  const res = await fetch(
    "https://fakeforge.com.br/api/generate?type=pessoa&quantity=20&format=json"
  );
  const { data } = await res.json();
  fixtures = data;
});

export function getFixture(index: number) {
  return fixtures[index % fixtures.length];
}

Para reprodutibilidade total em CI, persista o JSON gerado como artefato de build e reutilize-o em reruns. Ver seção de pipeline abaixo.

---

CPF, CNPJ e documentos brasileiros em testes

Algoritmo mod-11 e por que CPFs aleatórios quebram validações

O CPF usa dois dígitos verificadores calculados por módulo 11. Um CPF com 11 dígitos aleatórios tem aproximadamente 1% de chance de passar na validação. Em uma suíte com 500 registros, isso gera 495 falhas silenciosas no campo de documento antes de qualquer lógica de negócio ser testada. Use /gerador-cpf para CPF válido para fixtures ou implemente o algoritmo mod-11 localmente como no exemplo acima.

CNPJ Alfanumérico (vigência 01/07/2026): impacto em suítes de teste existentes

A Receita Federal publicou a IN RFB 2.229/2024 que autoriza CNPJ com letras nas 8 primeiras posições a partir de 01/07/2026. Regex que aceita apenas \d{14} vai rejeitar CNPJs novos. Atualize fixtures e validadores antes da data de vigência. O gerador de CNPJ Alfanumérico já gera no novo formato para você testar a migração.

Outros documentos: RG, CNH, PIS/PASEP, Título de Eleitor

Cada documento tem regra de formação própria. O gerador de CNH aplica o algoritmo DENATRAN. O gerador de PIS/PASEP usa mod-11 com pesos específicos. O Título de Eleitor inclui código de zona e seção. Números aleatórios nesses campos falham em sistemas que fazem qualquer validação de formato.

---

Dados financeiros sintéticos em ambiente de teste

Chaves PIX válidas sem CPF/CNPJ real

A chave PIX do tipo CPF ou CNPJ precisa de documento válido para ser aceita em validações. Use /gerador-pix para gerar chaves dos quatro tipos (CPF, CNPJ, e-mail, celular, aleatória) com formato correto para não travar fluxos de pagamento em testes de integração.

Cartões com Luhn correto (Visa, Mastercard, Elo, Hipercard)

O algoritmo de Luhn valida se um número de cartão é estruturalmente possível. Um número aleatório de 16 dígitos falha em qualquer biblioteca de validação de cartão. O gerador de cartão cobre Visa, Mastercard, Elo e Hipercard com BIN correto e Luhn válido.

Contas bancárias: códigos COMPE reais, números fictícios

Conta bancária precisa de código COMPE real (341 para Itaú, 260 para Nubank, 077 para Inter). Números de agência e conta são fictícios, mas o par banco+formato-de-conta deve ser válido para não rejeitar na camada de validação de formulário. O gerador de conta bancária usa os 17 códigos COMPE do catálogo do BACEN.

---

Pipeline de CI/CD conforme com a LGPD

Separar variáveis de ambiente: produção vs. staging vs. teste

# .github/workflows/test.yml
jobs:
  test:
    runs-on: ubuntu-latest
    env:
      DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
      # Nunca: DATABASE_URL: ${{ secrets.PRODUCTION_DATABASE_URL }}
    steps:
      - uses: actions/checkout@v4
      - name: Gerar fixtures
        run: |
          curl -s "https://fakeforge.com.br/api/generate?type=pessoa&quantity=100&format=json" \
            > tests/fixtures/pessoas.json
      - name: Seed banco de testes
        run: npm run db:seed
      - name: Executar testes
        run: npm test

Gerar fixtures na build, nunca fazer dump de produção

A geração em build time resolve dois problemas: nenhum dado real entra no pipeline e as fixtures ficam registradas como artefato rastreável. Guarde o artefato por 30 dias para debugging, mas nunca promova para o próximo ambiente sem regeração.

Auditoria de acesso ao banco de testes

Liste quem tem credenciais do banco de testes com a mesma disciplina que lista acesso à produção. Banco de teste com dados mascarados que vazam para notebook pessoal ainda é incidente de segurança se os dados permitem reidentificação.

---

API de dados sintéticos como dependência de projeto

Chamar uma API vs. biblioteca local: trade-offs

CritérioAPI externa (FakeForge)Biblioteca local
Atualização de regras (ex: CNPJ alfanumérico)AutomáticaRequer atualização de dependência
Disponibilidade offlineNãoSim
Rastreabilidade de origemVia log de chamadaNenhuma
Volume de geraçãoLimitado por planoIlimitado local
Manutenção do algoritmoTerceiroSeu time

Para equipes com alto volume de geração em CI, os planos da API partem de R$29/mês com 10.000 chamadas/dia.

Geração em tempo de build x geração em tempo de execução

Geração em build time é preferível para estabilidade: o mesmo artefato roda em todos os ambientes do pipeline. Geração em execução é útil para testes de stress onde volume e diversidade importam.

Controle de volume: quantos registros seu pipeline realmente precisa

Equipes tendem a supersuprir fixtures "por precaução". 20 registros cobrem a maioria das combinações de edge case em testes unitários. 500 cobrem integração com paginação e filtros. Acima disso, avalie se o teste em questão é de carga, que tem requisitos diferentes de dados.

---

Documentação de conformidade: o que registrar

Mapeamento de dados (ROPA) inclui ambientes de teste?

Sim. O Registro das Operações de Tratamento (ROPA), exigido pelo Art. 37 da LGPD e pelo Art. 30 do GDPR para controladores com obrigação espelhada, deve incluir todos os ambientes onde dados pessoais transitam. Se o banco de testes tem dados reais ou pseudonimizados, ele entra no ROPA.

Evidências para auditoria: fixture origin, anonimização aplicada, data

Para cada ambiente de teste, documente: origem dos dados (sintético/mascarado), técnica aplicada (geração sintética/mascaramento campo a campo), data de geração e quem executou. Um arquivo fixtures/README.md com essas informações já satisfaz auditoria inicial da ANPD.

Relatório de Impacto à Proteção de Dados (RIPD): quando o teste obriga

O RIPD (Art. 38 da LGPD) é obrigatório para tratamentos que podem gerar risco aos titulares. Testes que usam dados reais de categorias sensíveis (saúde, biometria, dados financeiros detalhados) provavelmente obrigam RIPD. Testes com dados sintéticos não tratam dados pessoais e não precisam de RIPD.

---

Resumo

  • Dados reais em teste são tratamento de dados pessoais sem base legal válida. Anonimização desde a origem é a única saída que elimina o problema pela raiz.
  • Pseudonimização não remove o dado do escopo da LGPD (Art. 13, §4º). Hash reversível ainda é dado pessoal.
  • CPF, CNPJ, PIX e cartões precisam de algoritmo correto (mod-11, Luhn) para não travar validações antes de qualquer lógica de negócio ser testada. Geradores como /gerador-cpf e /gerador-cnpj resolvem isso sem código extra.
  • CNPJ Alfanumérico entra em vigor em 01/07/2026. Qualquer regex \d{14} vai rejeitar CNPJs novos. Atualize fixtures e validadores agora usando /gerador-cnpj-alfanumerico.
  • Pipeline de CI/CD deve gerar fixtures na build, nunca promover dump de produção. Artefato gerado em build time é rastreável e não contém dados pessoais.
  • Documente a origem dos seus fixtures (sintético vs. mascarado, data, responsável). Uma linha por ambiente no ROPA já coloca seu time à frente de 90% das equipes em uma auditoria da ANPD.

Perguntas frequentes

Staging com dados reais é tão arriscado quanto produção?+

Mais arriscado, na verdade. Staging tem menos controle de acesso (estagiários, freelancers), então a exposição é maior. A LGPD não diferencia ambiente. Se o dado é real e foi copiado sem finalidade legítima, é incidente independente da camada. Use sintético em staging também.

Se descobrirmos que usamos dados reais, precisamos notificar a ANPD?+

Só se houve acesso não autorizado confirmado. Usar seu próprio dado de produção em teste da sua empresa não é acesso não autorizado. Porém, se o dump vazou (e-mail, GitHub público), aí sim: comunicar à ANPD em até 72h (Resolução CD/ANPD 2/2022). Documentar o incidente internamente já.

FakeForge tem limite de requisições no plano gratuito?+

A interface web é livre. Endpoints da API entram nos planos pagos a partir de R$29/mês com 10.000 chamadas/dia. Para fixtures em CI/CD local com volume baixo (até 100 registros), a chamada GET simples pode ficar dentro de quotas de rate limit. Confira `/pricing` antes de escalar.

Posso usar fixture com CPF válido mas endereço aleatório?+

Não. CPF gerado sinteticamente é ok. Mas se você pega endereço real de uma API de CEP e combina com CPF sintético, criou um pseudo-registro potencialmente reidentificável (CEP+bairro+nome = rastreável). FakeForge já correlaciona: use pessoa completa ou implemente a correlação localmente.

Como validar que meu seed de teste realmente cobre os edge cases?+

Gere 20+ registros com FakeForge, mas priorize casos deliberados (CPF mod-11 limítrofe, CEP de fronteira, DDD raro). Seed determinístico permite reruns idênticos. Combine seed gerado com fixtures hand-coded para edge cases. Cobertura ≠ aleatoriedade. Veja `beforeAll` no artigo para factory padrão.