Unidade 6 / 11

Geração e testabilidade de testes unitários: testes robustos com IA

Ganhos:

  • Capacidade de impedir que a inteligência artificial aceite comportamento errôneo como “correto”, calculando o valor esperado em testes unitários independentemente da regra de aceitação
  • Capacidade de imprimir testes rápidos, independentes e repetíveis, aplicando os princípios AAA e FIRST e simulando dependências externas
  • Capacidade de testar testes com mutação (quebra de código) e reconhecer código difícil de testar como um cheiro de design

A maior e mais rápida camada da pirâmide de testes é o teste unitário – teste que verifica uma função ou um pequeno trecho de código isoladamente de todo o resto. Milhares de testes unitários são executados em segundos e detectam um bug enquanto o código ainda está na tela do desenvolvedor. A inteligência artificial (IA) é talvez mais proficiente na produção de testes unitários: você atribui uma função a ela, a IA produz dezenas de testes. Mas esta mesma conveniência dá origem à maior armadilha: a IA produz facilmente testes que “brilham em verde, mas não verificam nada” ou aceitam o comportamento atual (talvez defeituoso) do código como “correto”. Nesta unidade você aprenderá como escrever testes de unidade verdadeiramente protetores com IA e a relação entre código testável e IA.

Qualidades de um bom teste unitário: PRIMEIRO

Bons testes de unidade seguem os princípios FIRST: Rápidos, Independentes (os testes não devem depender uns dos outros), Repetíveis (repetíveis - mesmo resultado em qualquer ambiente), Autovalidados (aprovação/reprovação clara), Oportunos (no prazo). Lembre-se desses princípios ao fazer com que a IA produza testes; peça especificamente que o teste não dependa do mundo exterior (banco de dados real, rede, relógio) para ser "independente" e "repetível".

Padrão AAA e afirmação expressiva

Um teste de unidade sólido segue a estrutura AAA: Arrange (preparar — configurar entradas e dependências), Act (executar — chamar a função em teste), Assert (validar — comparar o resultado com o valor esperado). O crítico é afirmar. O erro mais comum que a IA comete é derivar a afirmação da saída do código em teste – a lógica “tudo o que o código retorna é verdadeiro”. Isso torna o teste sem sentido. A forma correta é determinar o valor esperado de forma independente (a partir dos critérios de aceitação, calculá-lo manualmente).

Atenção: Se você disser à IA "escreva um teste para esta função", a IA poderá executar a função e escrever sua saída como "esperada". Este teste passa mesmo se a função for falsa. Em vez disso, diga "você calcula os resultados esperados de acordo com essas regras, não faça referência à saída atual da função".

Simulações, stubs e dependências

O teste unitário requer isolamento. Se sua função depende de um banco de dados ou API, eles serão substituídos por objetos simulados (mock/stub — um substituto fictício e controlado para a dependência real) nos testes. Isto torna o teste rápido, independente e reprodutível. A IA pode produzir instalação simulada; Mas cuidado com a zombaria excessiva: se você zombar de tudo, o teste verificará apenas "o que a simulação retorna", e não a lógica real. Equilíbrio: emule o mundo exterior, execute a lógica real em teste.

Testabilidade e IA

Há um feedback interessante: o código difícil de testar geralmente é um código mal projetado. Se a IA tiver problemas para escrever testes em uma função (muitas dependências, estado global oculto, efeitos colaterais), isso é um cheiro de design. Perguntar à IA “como você refatoraria este código para torná-lo testável” leva a testes e códigos melhores.

Testes parametrizados e diversidade de dados

Escrever um teste separado a cada vez para verificar a mesma regra com entradas diferentes é tedioso e difícil de manter. O teste parametrizado – uma estrutura que executa repetidamente a mesma lógica de teste em uma lista de entradas e resultados esperados – elimina essa repetição: um único corpo de teste é alimentado com dezenas de pares de entradas. A IA é muito eficiente na produção dessas tabelas de resultados esperados quando você fornece suas regras de aceitação; Em particular, tabula sistematicamente valores limites e classes de equivalência.

Mas aqui também há uma armadilha: a IA tende a derivar os resultados esperados na tabela gerada a partir do código em teste. Este erro é ainda mais perigoso em testes parametrizados, porque uma única lógica incorreta invalida dezenas de linhas. Portanto, tenha sempre a coluna de resultado esperado calculada de forma independente de acordo com a regra de aceitação e valide manualmente pelo menos algumas linhas. Peça também uma coluna de descrição "o que cada linha representa"; então, quando uma linha é quebrada, você vê instantaneamente qual estado está quebrado.

Dica: adicione intencionalmente uma “linha trap” à tabela de teste parametrizada – ou seja, digite o resultado incorretamente. Se essa linha não ficar vermelha quando você executar o teste, seu teste não está realmente verificando essa situação. Esta é uma verificação rápida de simulação.

Alerta fraco / Alerta forte

Fraco: "Escreva um teste de unidade para esta função."
Forte: Escreva testes de unidade [linguagem/estrutura] para a função "taxCalculate(amount, rate). Regra de aceitação: resultado = valor * taxa, arredondado para 2 decimais; valor ou taxa negativa gera um erro; retorna 0 se a taxa for 0. Use a estrutura AAA. Calcule manualmente os valores esperados de acordo com ESTAS regras; não faça referência à saída atual da função. Cubra casos vinculados e negativos (0, negativo, muito grande, arredondado para decimais). Deixe o nome de cada teste descrever a regra. verifica. Dependência externa "Não."

Alerta poderoso; Ele fornece a regra de aceitação, expectativa de valor esperado independente, estrutura e casos extremos. Assim, o teste torna-se o guardião da regra e não o espelho do código.

Tabela de qualidade de teste unitário

sintoma

Teste ruim (confiança falsa)

bom teste

afirmar

Nenhum ou "não nulo"

Valor concreto esperado

Fonte de valor esperado

Saída da função

Regra de aceitação/cálculo manual

vício

Banco de dados real/rede/hora

Isolado com mock/stub

caso extremo

Apenas estrada feliz

limite, negativo, erro

Quando você quebra o código

permanece verde

fica vermelho

Nome

teste1, método de teste

descreve a regra que confirma

Quatro modelos copiáveis

1) Teste de unidade baseado em regras:

Sua função: engenheiro sênior de teste de software.Escreva um teste de unidade na seguinte função com [linguagem/estrutura]: [assinatura].Regras de aceitação: [regras].- Use estrutura AAA.- Calcule manualmente os valores esperados de acordo com ESTAS regras; NÃO faça referência à saída atual da função. - Cubra o limite, negativo, erro e caminho feliz com testes separados. - Deixe que cada nome de teste descreva a regra que ele verifica. - Simular dependências externas; Faça a lógica real funcionar.

2) Controle de resistência à mutação:

Confira esses testes de unidade. Liste 5 pequenos ajustes que eu poderia fazer no código em teste (a - em vez de +, a >= em vez de a >, uma mudança de limite) e diga-me para cada um QUAL desses testes ficará vermelho? Se nenhum for retornado, o teste é insuficiente.Código + testes: [colar]

3) Revisão de testabilidade:

Por que é difícil escrever um teste unitário para esta função? Vício oculto, status global, efeitos colaterais, existem muitas responsabilidades? Sugira refatoração mínima para torná-lo testável; não mude o comportamento. Código: [colar]

4) Conclusão incompleta do cenário:

A seguinte função e os testes disponíveis são fornecidos. Liste qual comportamento/edgecase NUNCA foi testado (lacuna de escopo) e adicione um teste para cada um. Função + testes: [colar]

três mini cases

Caso 1 — Teste o espelhamento do código. Um desenvolvedor fez com que a IA escrevesse um teste para a função de arredondamento; 10 testes foram verdes. Na verdade, a função estava arredondando na direção errada, mas a IA havia retirado os valores esperados da saída da função, então os testes consideraram o erro “verdadeiro”. Quando os valores esperados foram calculados manualmente com o modelo “orientado por regras”, 4 testes ficaram vermelhos e o erro real foi revelado.

Caso 2 — O valor do controle de mutação. Uma equipe contou com 45 testes unitários. Tentei 20 pequenos ajustes no código com uma "verificação de robustez de mutação"; os testes detectaram apenas 11 deles. As 9 interrupções restantes passaram silenciosamente. A equipe fortaleceu os testes fracos; Um erro de cálculo real foi detectado por esses testes aprimorados na próxima versão.

Caso 3 — A não testabilidade é um cheiro de design. A IA não conseguia escrever testes para uma função de pedido, ela precisava constantemente do banco de dados real. O modelo de "revisão de testabilidade" mostrou que a função de acesso ao banco de dados incorporado. Quando a injeção de dependência foi removida, os testes puderam ser escritos e o código ficou mais limpo.

Erros comuns

  • Derivando o valor esperado do código. A IA aceita a saída da função como “correta”; teste que confirma código defeituoso.
  • Teste sem afirmação ou com afirmação trivial. Lógica “Ele não errou, ele passou”; Não confirma nada.
  • Simulação extrema. Zombando de tudo e testando apenas o que o mock retorna; a lógica real não é testada.
  • Apenas a estrada feliz. Ignorando estados limites, negativos e de erro.
  • Não testando quebrando o código. Confiar no verde sem verificar se há mutação.
  • Ignorando a intestabilidade. Não reconhecer e corrigir projetos ruins em vez de realizar testes rigorosos.

Em resumo

Os testes unitários são a maior e mais rápida camada da pirâmide de testes; Ele detecta o erro no momento mais barato. A IA é muito capaz de produzir testes unitários, mas sua maior armadilha é escrever testes que assumem o comportamento incorreto como “correto”, derivando o valor esperado do próprio código. Solução: fornecer as regras de aceitação, calcular manualmente os valores esperados, aplicar os princípios AAA e FIRST, zombar do mundo exterior e executar a lógica real e testar cada teste por mutação (quebrando o código). Código difícil de testar é um sinal de design que precisa ser corrigido.

Tarefa de aplicativo

Selecione uma função que contenha uma regra de negócios do seu próprio projeto. Escreva regras de aceitação e faça com que a IA escreva testes com o modelo “teste de unidade orientado a regras”; Tenha os valores esperados calculados manualmente. Em seguida, aplique a “verificação de robustez de mutação”: faça pelo menos 5 pequenas quebras no código e meça quantos testes ficam vermelhos. Adicione novo teste para corrupções não detectadas. Relate quantas interrupções foram detectadas (como pontuação de mutação).

lista de verificação

  • [ ] Forneci as regras de aceitação e mandei calcular os valores esperados manualmente.
  • [] Certifiquei-me de que os testes não derivassem o valor esperado do código.
  • [] Eu estabeleci testes independentes seguindo as diretrizes AAA e FIRST.
  • [] Zombei das dependências externas e executei a lógica real.
  • [] Cobri casos de limite, negativo e erro.
  • [] Ao quebrar o código (mutação) provei que os testes realmente protegem.