Como Criar um Harness de Testes para Código Gerado por IA
Imagine a seguinte cena: você pede para um assistente de IA gerar uma função de validação de CPF, ou um endpoint de API, ou uma rotina de cálculo financeiro. O código chega rápido, bem formatado, com nomes de variáveis sensatos e até comentários explicativos. Parece pronto. Você cola no projeto, executa manualmente uma ou duas vezes, vê que “funciona” e segue em frente. Semanas depois, um caso de borda que ninguém testou explode em produção — e a investigação revela que aquele trecho gerado pela IA nunca foi validado de verdade, apenas validado visualmente.
Esse cenário se repete com uma frequência preocupante nas equipes que adotaram ferramentas como Claude Code, Cursor, GitHub Copilot ou qualquer outro assistente de geração de código. O problema não é a IA ser ruim — é que código gerado automaticamente precisa ser tratado com o mesmo rigor que código de um desenvolvedor júnior no primeiro dia: promissor, mas não confiável até prova em contrário. E “prova em contrário”, em engenharia de software, tem nome: harness de testes.
Neste artigo vamos construir, passo a passo, um harness de testes pensado especificamente para validar código gerado por IA, com exemplos em múltiplas linguagens, estratégias de cobertura e uma estrutura que você pode adaptar ao seu stack — seja Python, TypeScript, Java, C# ou Delphi.
O que é um Harness de Testes
Um harness de testes (do inglês test harness) é o conjunto de ferramentas, scripts, mocks, dados de entrada e infraestrutura que permite executar testes automatizados de forma consistente e repetível, sem intervenção manual. Pense nele como uma bancada de testes de laboratório: você não testa um componente eletrônico apertando fios com a mão a cada vez — você monta uma bancada com multímetro, fonte de alimentação e osciloscópio configurados uma vez, e depois só troca o componente sob teste.
No contexto de código gerado por IA, o harness cumpre um papel ainda mais crítico do que em desenvolvimento tradicional, por três motivos:
- Alucinação silenciosa: a IA pode gerar código sintaticamente correto que resolve um problema ligeiramente diferente do que você pediu.
- Falsa confiança: código bem formatado e com nomes descritivos passa uma sensação de qualidade que nem sempre corresponde à correção lógica.
- Volume: quando você gera código em maior velocidade, precisa validar em maior velocidade também — testes manuais não escalam.
Diferença entre Harness e Suíte de Testes Comum
Uma suíte de testes tradicional geralmente é escrita pensando no comportamento esperado de código que você mesmo projetou. Um harness para código de IA precisa ser mais desconfiado por padrão: ele assume que o código pode estar sutilmente errado mesmo quando “parece” certo, e por isso incorpora camadas adicionais de verificação, como testes de propriedades, testes de regressão automática e validação de contratos de entrada e saída.
Como Funciona: Anatomia de um Harness para Código de IA
Um harness robusto para esse cenário costuma ter quatro camadas:
- Testes unitários determinísticos: casos conhecidos com entrada e saída esperada, incluindo casos de borda.
- Testes de propriedades (property-based testing): em vez de fixar exemplos, você define invariantes que devem ser verdadeiras para qualquer entrada válida.
- Testes de contrato: validam tipos, formatos e limites de entrada/saída, especialmente úteis quando a IA gera APIs ou integrações.
- Sandbox de execução isolada: ambiente controlado (container, processo isolado ou VM) onde o código gerado roda sem acesso a recursos sensíveis, evitando efeitos colaterais indesejados durante a validação.
Passo 1: Isolar o Código Gerado em uma Interface Clara
O primeiro erro comum é colar o código da IA diretamente misturado com o restante da aplicação. Isso dificulta testar isoladamente. A prática recomendada é sempre gerar código atrás de uma interface ou contrato bem definido, mesmo que a implementação venha de um prompt.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
// contrato.ts export interface ValidadorDocumento { validar(documento: string): boolean; } // gerado-pela-ia.ts import { ValidadorDocumento } from './contrato'; export class ValidadorCPF implements ValidadorDocumento { validar(cpf: string): boolean { const numeros = cpf.replace(/\D/g, ''); if (numeros.length !== 11 || /^(\d)\1{10}$/.test(numeros)) { return false; } // lógica de validação dos dígitos verificadores... return true; } } |
Com o contrato definido, o harness testa contra a interface, não contra a implementação específica — o que permite trocar a implementação gerada pela IA sem quebrar a suíte de testes.
Passo 2: Testes Unitários com Casos de Borda Explícitos
Peça sempre para a IA listar os casos de borda que ela considerou, e depois adicione os que ela não considerou. Usando Jest como exemplo:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import { ValidadorCPF } from '../gerado-pela-ia'; describe('ValidadorCPF gerado por IA', () => { const validador = new ValidadorCPF(); test('rejeita CPF com todos os dígitos iguais', () => { expect(validador.validar('111.111.111-11')).toBe(false); }); test('rejeita CPF com menos de 11 dígitos', () => { expect(validador.validar('123.456.789')).toBe(false); }); test('aceita CPF válido conhecido', () => { expect(validador.validar('529.982.247-25')).toBe(true); }); test('rejeita string vazia', () => { expect(validador.validar('')).toBe(false); }); test('rejeita entrada com caracteres não numéricos misturados incorretamente', () => { expect(validador.validar('abc.def.ghi-jk')).toBe(false); }); }); |
Esses testes já pegariam boa parte dos erros típicos de implementações geradas às pressas, como esquecer de tratar CPFs com dígitos repetidos (um erro clássico que passa despercebido em revisões visuais).
Passo 3: Testes de Propriedades para Cobrir o Que Você Não Pensou
Testes de exemplo fixo cobrem apenas os casos que você imaginou. Testes de propriedades geram centenas de entradas aleatórias e verificam invariantes. Em Python, a biblioteca Hypothesis é a referência:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
from hypothesis import given, strategies as st from validador_gerado_pela_ia import calcular_desconto @given(preco=st.floats(min_value=0.01, max_value=1_000_000), percentual=st.floats(min_value=0, max_value=100)) def test_desconto_nunca_e_negativo(preco, percentual): resultado = calcular_desconto(preco, percentual) assert resultado >= 0 @given(preco=st.floats(min_value=0.01, max_value=1_000_000), percentual=st.floats(min_value=0, max_value=100)) def test_desconto_nunca_supera_o_preco_original(preco, percentual): resultado = calcular_desconto(preco, percentual) assert resultado <= preco |
Se a função gerada pela IA tiver um erro de sinal ou uma divisão mal posicionada, o Hypothesis encontra o contraexemplo automaticamente e reporta a entrada exata que quebrou a invariante — algo praticamente impossível de descobrir só olhando o código.
Passo 4: Sandbox de Execução Isolada
Quando o código gerado envolve acesso a arquivos, rede ou processos do sistema, é fundamental executá-lo em um ambiente isolado antes de confiar nele. Containers são a ferramenta natural para isso. Um exemplo simples com Docker, rodando os testes em um container descartável:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
FROM python:3.12-slim WORKDIR /app COPY . . RUN pip install --no-cache-dir -r requirements.txt # Sem acesso à rede durante os testes, sem volumes persistentes montados CMD ["pytest", "--maxfail=1", "--disable-warnings", "-q"] |
|
1 2 3 4 5 |
docker build -t harness-ia . docker run --rm --network none harness-ia |
A flag --network none garante que, se o código gerado tentar fazer uma chamada externa inesperada (um comportamento que às vezes aparece quando a IA “inventa” uma dependência ou tenta buscar dados de uma API imaginária), o teste falhe de forma controlada em vez de causar efeitos colaterais reais.
Passo 5: Automatizando no Pipeline de CI
O harness só tem valor real se rodar automaticamente a cada geração ou alteração de código. Um exemplo de workflow com GitHub Actions:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
name: harness-codigo-ia on: pull_request: paths: - 'src/gerado-por-ia/**' jobs: validar: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Configurar Node uses: actions/setup-node@v4 with: node-version: '20' - name: Instalar dependências run: npm ci - name: Rodar testes unitários e de propriedades run: npm test -- --coverage - name: Verificar cobertura mínima run: npx nyc check-coverage --lines 85 --functions 85 --branches 75 |
Note o gatilho baseado em paths: o pipeline dispara especificamente quando arquivos na pasta reservada para código gerado por IA são alterados, tornando explícito que esse código passa por um crivo mais rigoroso.
Exemplo em Outro Ecossistema: Delphi
Para quem trabalha com Delphi, o mesmo princípio se aplica usando DUnitX como framework de testes:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
unit TesteValidadorCPFGeradoPorIA; interface uses DUnitX.TestFramework, ValidadorCPFGeradoPorIA; type [TestFixture] TTesteValidadorCPF = class public [Test] procedure RejeitaCPFComDigitosRepetidos; [Test] procedure AceitaCPFValidoConhecido; end; implementation procedure TTesteValidadorCPF.RejeitaCPFComDigitosRepetidos; begin Assert.IsFalse(TValidadorCPF.Validar('111.111.111-11')); end; procedure TTesteValidadorCPF.AceitaCPFValidoConhecido; begin Assert.IsTrue(TValidadorCPF.Validar('529.982.247-25')); end; initialization TDUnitX.RegisterTestFixture(TTesteValidadorCPF); end. |
A estrutura conceitual é idêntica à do TypeScript: isolar em interface, cobrir casos de borda, automatizar a execução. A linguagem muda, o rigor não.
Checklist Prático para seu Harness
- Todo código gerado por IA entra atrás de uma interface testável.
- Existem testes unitários cobrindo pelo menos os casos de borda óbvios (vazio, nulo, limite, valores negativos, duplicados).
- Há pelo menos uma camada de teste de propriedades para funções com lógica não trivial.
- Execução acontece em ambiente isolado quando há acesso a rede, arquivos ou processos externos.
- O pipeline de CI bloqueia merge se a cobertura cair abaixo de um limiar definido pela equipe.
- Revisão humana continua existindo — o harness reduz risco, não substitui julgamento técnico.
Aprofunde-se na Comunidade Dev’s AI
Construir harnesses de teste para código gerado por IA é uma habilidade que se aprimora na prática, discutindo casos reais com outros desenvolvedores que enfrentam os mesmos desafios. Na Comunidade Dev’s AI compartilhamos templates de harness, discutimos ferramentas como Hypothesis, DUnitX, Jest e pytest aplicadas a fluxos com IA, e trocamos experiências reais de projetos que já rodam em produção com código assistido por assistentes como Claude Code e Cursor. Se você quer parar de confiar no “parece certo” e passar a confiar no “está provado”, entre para a comunidade e participe das discussões.
Conclusão
Código gerado por IA não é nem mais nem menos confiável por natureza do que código escrito por um humano — mas carrega um tipo específico de risco: a aparência de correção sem a garantia de correção. Um harness de testes bem construído neutraliza esse risco transformando a confiança subjetiva (“parece bom”) em verificação objetiva (“passou em N casos, N propriedades e rodou isolado sem efeitos colaterais”).
A boa notícia é que as ferramentas para isso já existem e são maduras, independentemente da linguagem que você usa. O trabalho real está em disciplina: isolar o código gerado atrás de contratos, escrever os casos de borda que a IA não pensou, adicionar testes de propriedades para as invariantes que realmente importam, e automatizar tudo isso no pipeline. Feito isso, você pode aproveitar a velocidade da geração assistida por IA sem herdar seus riscos silenciosos.