A barra verde que não prova nada
Existe um momento que todo time conhece e ninguém questiona: os testes passam, a barra fica verde e todo mundo respira aliviado. Verde quer dizer pronto. Verde quer dizer seguro. Verde quer dizer que alguém pensou no assunto e o sistema concordou. Essa cor discreta carrega uma quantidade impressionante de confiança — e essa confiança se apoia numa premissa silenciosa que se sustentou por décadas: a de que quem escreveu o teste e quem escreveu o código chegaram ao mesmo resultado de forma independente. Dois caminhos, um destino. Quando eles se encontram no meio, esse encontro é uma prova.
Essa premissa desmorona no instante em que uma máquina escreve os dois. O modelo produz a função e, no mesmo fôlego, produz os testes que verificam essa função. Ambos nascem do mesmo entendimento da tarefa, da mesma leitura do requisito, dos mesmos pontos cegos. O teste deixa de verificar se o código está correto. Ele verifica se o código faz o que o modelo achou que ele deveria fazer — e essa é uma afirmação completamente diferente, muito mais fraca.
A armadilha da validação circular
Imagine o caso mais simples. Pede-se a um modelo que escreva uma função para calcular um desconto. Ele lê o requisito, entende errado de forma sutil — digamos que aplica o desconto antes do imposto quando deveria vir depois — e escreve um código limpo e plausível contendo exatamente esse erro. Em seguida, pedimos ao mesmo modelo que escreva os testes. Ele os escreve contra o próprio entendimento. O teste espera o desconto antes do imposto. O código entrega o desconto antes do imposto. Verde.
O teste funcionou — no sentido de que rodou e passou. Mas não verificou nada. Não encontrou o bug, porque veio da mesma fonte que o bug. Não expôs o mal-entendido; ele o congelou, lacrou e coroou com um selo verde. Essa é a armadilha da validação circular: quando a mesma parte fornece tanto a afirmação quanto a prova da afirmação, a prova não vale nada. Ninguém pode ser testemunha da própria correção.
O cerne da questão cabe numa única frase: verde não é a mesma coisa que correto. Verde significa apenas que dois artefatos concordam entre si. Quando os dois vêm da mesma fonte com a mesma leitura equivocada, essa concordância não é novidade. É uma tautologia. Dá para alcançar sem esforço 100% de cobertura do comportamento errado desse jeito — cada linha exercitada, cada ramo testado, cada asserção satisfeita, e o software continua calculando o desconto errado. A métrica brilha enquanto o produto mente.
O perverso é que, de fora, isso parece exatamente uma garantia de qualidade funcionando. A mesma suíte de testes, o mesmo pipeline verde, os mesmos percentuais no relatório. Só que a verificação perdeu sua independência — a única propriedade que faz de um teste um teste. Um teste que nunca consegue pegar nada que seu autor já não acreditasse não é uma rede de segurança. É um espelho.
Testes como especificação executável
Se o modelo não pode mais ser seu próprio examinador, a independência precisa vir de outro lugar. Precisa vir de um humano. E o ponto em que ela entra no processo não é a implementação — essa a máquina pode assumir à vontade —, mas a especificação: a declaração precisa e vinculante do que "correto" significa neste caso específico.
A mudança de perspectiva é fundamental. Um teste deixa de ser uma prova a posteriori de que o código funciona e passa a ser a definição, feita de antemão, do que funcionar sequer significa. O desconto vem depois do imposto. Um valor negativo é inválido e deve ser rejeitado. Um carrinho sem itens não gera desconto algum — nem nulo, nem erro, mas exatamente US$ 0. Isso não são detalhes de implementação. São decisões de negócio — e decisões de negócio pertencem ao humano, porque só o humano responde pelas consequências.
Na prática, isso não significa necessariamente que um humano digite cada linha de cada teste à mão. Significa que um humano é dono do contrato de comportamento. Existe um espectro, e todo ponto nele é legítimo, desde que a autoria permaneça clara:
- O humano escreve as asserções centrais — as poucas linhas que fixam o que precisa acontecer — e deixa a máquina montar toda a mecânica de teste ao redor: fixtures, mocks, setup.
- O humano formula o contrato em prosa, preciso e completo, e deixa o modelo traduzi-lo em testes — mas então revisa essa tradução linha por linha, com o mesmo rigor com que a teria escrito.
- O humano define os exemplos que importam: os pares concretos de entrada e saída em que o comportamento se apoia. O modelo pode desenvolvê-los, mas os pares em si vêm do julgamento humano sobre o domínio.
O fio condutor é sempre o mesmo: a definição do que é correto não pode vir da mesma fonte que a implementação. No instante em que vem, a verificação é circular e a barra verde é oca. A suíte de testes então se torna o que sempre deveria ter sido e raramente foi na era humana: a especificação executável e independente do sistema. O código é uma resposta a ela, não seu coautor.
Propriedades, não exemplos: testes baseados em propriedades e invariantes
Existe uma classe de testes estruturalmente mais resistente à armadilha circular — porque é mais difícil de "burlar", seja de propósito, seja por acidente. Testes baseados em exemplos dizem: "Para a entrada X, espero a saída Y." Um modelo que escreve o código e o teste escolhe X e Y a partir do mesmo entendimento e encontra a si mesmo. Testes baseados em propriedades dizem algo fundamentalmente diferente: "Para qualquer entrada possível, esta propriedade deve valer."
Essa é a ideia por trás de ferramentas no estilo do Hypothesis ou do QuickCheck: em vez de descrever um único exemplo, você descreve uma invariante — uma verdade sobre o sistema que precisa valer sempre, não importa que dados entrem — e o framework atira centenas de entradas geradas aleatoriamente contra ela, incluindo de propósito as feias: o conjunto vazio, o número gigante, o caos de Unicode, o limite negativo. Quando um caso quebra a invariante, ele o reduz ao menor contraexemplo possível e o joga aos seus pés.
Por que é mais difícil de burlar? Porque uma invariante é formulada num nível mais alto que a implementação. "O valor retornado nunca é negativo." "Criptografar e depois descriptografar devolve o original." "A lista ordenada tem os mesmos elementos que a não ordenada." "Executar a mesma operação duas vezes não muda nada em relação a executá-la uma vez" — idempotência. Frases assim capturam classes inteiras de bugs em que ninguém pensou explicitamente, porque não consultam um exemplo, afirmam uma verdade. O modelo não consegue simplesmente moldar seu código aos casos de teste, porque não conhece os casos de teste — eles são sorteados em tempo de execução.
O contraste com a outra ponta do espectro não poderia ser mais nítido. Testes de snapshot simplesmente congelam o comportamento atual: na primeira execução, o teste grava o que o código produz e, dali em diante, dispara sempre que a saída muda. Isso é útil contra regressões acidentais — mas não verifica correção nenhuma. Um snapshot de um bug é um bug congelado, solenemente lacrado e defendido contra qualquer correção. Quando um modelo produz tanto o código quanto o snapshot, você tem o teste circular em sua forma mais pura: "o código faz o que o código faz." Snapshots são uma ferramenta, não uma prova, e confundi-los com verificação é um dos erros mais caros numa base de código dominada por IA.
Do percentual de cobertura ao requisito de verdade
A mudança mais importante, no fim das contas, não é técnica, mas uma troca da pergunta que se está avaliando. Por décadas a métrica-guia foi: que percentual do código a suíte de testes cobre? Esse número nunca foi lá muito significativo, mas na era da IA ele se torna ativamente enganoso, porque um modelo produz sem esforço os testes que tocam cada linha sem que um único deles verifique um requisito de verdade. Cobertura mede o que foi executado, não o que foi assegurado. 100% de uma mentira continua sendo mentira.
A pergunta que importa não é mais "quanto o teste cobre?", mas "este teste codifica um requisito de verdade?". Existe, por trás desta asserção, uma afirmação sobre o negócio, sobre o comportamento esperado, sobre algo que doeria se virasse do avesso — ou ela apenas toca uma linha para a estatística fechar? Um único teste que fixa uma invariante real vale mais que cem que fabricam cobertura. E quando a métrica muda, muda também para onde os humanos apontam sua atenção escassa.
Essa atenção pertence, antes de tudo, às bordas que o modelo pula. O código gerado descreve o caminho feliz com eloquência e omite os cantos: a lista vazia, o valor negativo, o fuso horário na virada do dia, o acesso concorrente, o timeout no meio da transação. São exatamente os casos que nenhum exemplo no material de treinamento enfatizou — e exatamente os casos em que sistemas morrem em produção. O humano que conhece o domínio sabe onde os corpos estão enterrados. O modelo não sabe.
Uma jogada eficaz é fazer o modelo gerar testes adversariais contra o próprio código: "Aqui está a implementação — escreva os casos de teste mais cruéis que você conseguir imaginar para quebrá-la." Isso é surpreendentemente produtivo, porque um modelo no papel destrutivo muitas vezes enxerga bordas que pulou no papel construtivo. Mas — e este é o alerta que vem direto da disciplina de revisão de código — ele compartilha os pontos cegos. O mesmo modelo, com o mesmo viés de treinamento, no papel de atacante, ignora a mesma classe de bugs que embutiu como autor. Ele encontra as bordas que conhece e passa reto pelas que nunca viu. Testes adversariais de IA são uma camada a mais, nunca a última. A última é um humano com um modelo de ameaça que nenhum modelo possui.
E existe uma ferramenta para verificar os próprios testes — para medir se eles são capazes de pegar alguma coisa. Teste de mutação altera de propósito o código de produção: troca um > por um >=, apaga uma linha, nega uma condição — e observa se algum teste fica vermelho por causa disso. Se tudo continua verde apesar da mutação infiltrada, a suíte não verifica nada ali: o teste passa pelo código sem cravar seu comportamento. Teste de mutação é a resposta mais honesta à pergunta "nossos testes de fato testam?" — e num mundo em que máquinas despejam testes em série, é o controle natural sobre a qualidade deles.
Para que servem os testes de IA, apesar de tudo
Agora o outro lado honesto, senão o argumento vira dogma. Nada disso quer dizer que testes gerados por IA sejam inúteis ou devam ser proibidos. Isso seria uma bobagem cara.
Como primeira camada, eles são genuinamente úteis. Pegam regressões — quando uma mudança posterior quebra um comportamento que antes estava certo, o teste gerado dispara, não importa quem o escreveu. Cuidam do boilerplate maçante: o setup, os fixtures, os vinte casos entediantes ao redor do único interessante. Alcançam em minutos uma cobertura de base que levaria horas para um humano. E — este é o prêmio de verdade — liberam o humano para as invariantes que importam. Quem não digita mais os fixtures fica com a cabeça livre para a única asserção que realmente conta. A divisão de trabalho não é "humano ou máquina", mas "máquina para volume, humano para significado".
O erro não é usar testes de IA. O erro é confundi-los com verificação — tratar a primeira camada como se fosse a última e parar no instante em que fica verde. Ninguém os proíbe; apenas se recusa a lhes entregar uma responsabilidade que eles não conseguem carregar.
E mais uma ressalva, para que a lição não seja esticada além da conta: nem tudo precisa de teste pesado. O cuidado deve escalar com o raio de destruição — com o que acontece, na pior das hipóteses, se este código estiver errado. Um script descartável que reformata um CSV uma única vez não precisa de suíte de invariantes. Um dashboard interno para cinco pessoas não é o fluxo de pagamento. O rigor total — especificações de propriedade humana, testes baseados em propriedades, teste de mutação — pertence a onde um erro dói de verdade: dinheiro, dados de clientes, autenticação, tudo que é irreversível. Testar tudo no máximo é sua própria forma de desperdício. A arte é direcionar a atenção humana escassa para onde a correção não é negociável.
Conclusão
Por décadas, o teste foi o selo que vinha depois do trabalho de verdade, o de escrever o código. Essa ordem está se invertendo. Quando a máquina escreve o código, o teste deixa de ser a prova de que funcionou — ele é a definição, feita de antemão e de responsabilidade humana, do que funcionar sequer significa. A suíte de testes é a especificação, e a especificação não pertence à máquina que a satisfaz.
É exatamente daqui que partimos na NH Labs. Deixamos a IA escrever o que se escreve bem: a implementação, o boilerplate, os vinte casos entediantes, a cobertura de base em minutos. Mas somos donos do contrato de comportamento — as asserções que cravam o que "correto" significa neste sistema, com estes dados, para este cliente. Um modelo pode produzir verde à vontade; o que verde pode significar, quem define é um humano. É por isso que nossos clientes podem confiar na barra verde: não porque uma máquina concordou consigo mesma, mas porque um humano decidiu de antemão o que a concordância precisa provar.