Nos capítulos anteriores você usou o Codex para entender, corrigir e refatorar código, sempre apoiado em uma suíte de testes que já existia. Agora vamos construir e manter essa suíte. Ao final deste capítulo você saberá pedir testes unitários e de integração para código existente, obrigar o agente a enumerar casos de borda antes de escrever qualquer asserção, conduzir um ciclo no estilo desenvolvimento guiado por testes, aumentar cobertura de forma dirigida e reconhecer testes ruins antes que eles entrem no repositório.
Primeiro: o agente precisa saber como rodar os testes
Um teste que o agente escreve mas nunca executa é só um texto plausível. A primeira providência é registrar no AGENTS.md o comando exato da suíte, o de um arquivo isolado e o de cobertura. Isso transforma a execução em hábito do agente, não em pedido seu a cada sessão.
## Testes
- Suite completa: `pytest -q`
- Um arquivo: `pytest -q tests/test_pedidos.py`
- Cobertura: `pytest --cov=app --cov-report=term-missing`
(exige o plugin `pytest-cov` instalado no ambiente)
- Framework: pytest. Nao usar unittest.
- Testes ficam em `tests/`, espelhando a estrutura de `app/`.
- Nomeie arquivos como `test_<modulo>.py` e funcoes como `test_<comportamento>`.
- Regra: apos qualquer edicao de codigo, rode a suite completa
e cole o resultado na resposta.
Como a suíte precisa rodar de verdade, a sessão deve estar em modo automático com sandbox de escrita na pasta de trabalho. Se os testes dependerem de baixar pacotes ou de um serviço externo, prepare o ambiente antes de abrir o Codex, para não precisar liberar acesso à rede no meio do caminho.
Gerando testes unitários para código existente
Um teste unitário exercita uma unidade pequena de código, normalmente uma função ou uma classe, isolada de banco de dados, rede e sistema de arquivos. É o tipo mais barato de escrever e o mais rápido de rodar, então é por onde começamos.
O erro comum é pedir testes para um módulo inteiro de uma vez. O resultado costuma ser um arquivo enorme, repetitivo e difícil de revisar. Prefira uma função por vez, com critérios de aceitação explícitos, como você aprendeu no capítulo de prompts.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
Objetivo: escrever testes unitarios para a funcao
calcular_frete em @app/servicos/frete.py.
Contexto: pytest, projeto ja configurado. A funcao recebe
peso em quilos, cep de destino e tipo de envio.
Restricoes:
- Nao altere nenhum arquivo fora de tests/.
- Nao use mocks para logica pura.
- Um teste por comportamento, nomes descritivos.
Criterios de aceitacao:
- Cobrir os tres tipos de envio suportados.
- Cobrir peso zero, peso negativo e cep invalido.
- Rodar `pytest -q tests/test_frete.py` e colar a saida.
Repare no escopo negativo: proibir alterações fora da pasta de testes evita que o agente "conserte" o código de produção para fazer um teste passar. Essa é uma regra de ouro ao gerar testes para código existente. O código é a referência; se algo parecer errado, você quer saber, não quer que seja silenciosamente ajustado.
Casos de borda: faça o agente listar antes de escrever
Um caso de borda é uma entrada no limite do que a função aceita ou logo fora dele: valor vazio, valor máximo, tipo inesperado, lista com um único elemento, data na virada do ano, texto com acento ou emoji. Modelos de linguagem tendem a escrever testes para o caminho feliz porque é isso que aparece com mais frequência nos exemplos. A correção é simples e muito eficaz: peça a enumeração antes da implementação.
Antes de escrever codigo, liste em formato de tabela os casos
de teste que voce considera relevantes para calcular_frete:
entrada, comportamento esperado, motivo. Inclua uma secao
"casos duvidosos" com situacoes em que o comportamento correto
nao esta claro no codigo. Nao edite arquivos ainda.
Você revisa a lista, corta o que não faz sentido, acrescenta o que faltou e só então autoriza a escrita. A seção de casos duvidosos é ouro: normalmente ela revela decisões de negócio que ninguém documentou. Responda a essas dúvidas antes, porque um teste escrito em cima de uma suposição errada congela o erro.
Testes de integração
Um teste de integração verifica várias partes funcionando juntas, por exemplo uma rota da API que grava no banco de dados e devolve uma resposta. Ele é mais lento e mais sensível ao ambiente, então precisa de instruções mais detalhadas sobre como o ambiente é montado.
Escreva testes de integracao para a rota POST /pedidos
em @app/rotas/pedidos.py.
Use o TestClient do FastAPI e um banco SQLite em memoria,
criado e destruido por fixture a cada teste.
Atencao: SQLite em memoria cria um banco novo a cada conexao;
use StaticPool para compartilhar a mesma conexao e substitua
a dependencia de sessao do app com dependency_overrides.
O TestClient exige o pacote httpx instalado.
Nao use o banco de desenvolvimento.
Cubra: criacao valida, payload invalido com 422,
estoque insuficiente com 409 e idempotencia de requisicao repetida.
Ao final rode a suite completa.
Uma fixture é o pedaço de código que prepara e limpa o cenário de um teste, como criar um banco temporário ou um usuário de exemplo. Peça explicitamente que cada teste seja independente: nada de um teste depender do dado criado pelo anterior. Essa dependência oculta é uma das principais fontes de falhas misteriosas quando a ordem de execução muda.
Um ciclo no estilo TDD com o Codex
Desenvolvimento guiado por testes, conhecido pela sigla em inglês TDD, é a prática de escrever o teste antes da implementação. Com um agente, esse ciclo ganha um benefício extra: o teste vermelho vira o critério de aceitação objetivo do trabalho dele. Faça em três pedidos separados.
- Escrever o teste que falha. "Crie em tests/test_cupom.py os testes para a regra de cupom de desconto descrita abaixo. Não implemente nada em app. Rode a suíte e mostre que os novos testes falham." Você confere que a falha é por funcionalidade ausente, e não por erro de importação ou de digitação.
- Implementar o mínimo. "Agora implemente a regra em app/servicos/cupom.py até que os testes passem. Não altere os arquivos de teste. Rode a suíte completa." A proibição de tocar nos testes é o que impede a trapaça mais comum, que é enfraquecer a asserção em vez de resolver o problema.
- Limpar. Com a suíte verde e um commit feito, peça a refatoração da implementação com as técnicas do capítulo anterior.
Se durante o passo dois o agente alterar um teste, recuse o diff e repita o pedido apontando a violação. Manter os três passos em mensagens distintas, com commit entre eles, é o que torna o processo revisável.
Aumentar cobertura de forma dirigida
Cobertura de testes é a porcentagem de linhas ou ramos do código que a suíte executa pelo menos uma vez. É um indicador útil e um péssimo objetivo isolado: dá para chegar a noventa por cento executando tudo sem verificar nada. Use a cobertura para encontrar buracos, não para produzir número.
O fluxo dirigido é este. Peça ao agente que rode o relatório de cobertura, liste os dez trechos não cobertos com maior risco, classificando por impacto de uma falha ali, e proponha testes só para os três primeiros. Trechos de tratamento de erro e de regras financeiras valem muito mais que getters triviais. Diga isso explicitamente no pedido.
Rode `pytest --cov=app --cov-report=term-missing`.
Liste os arquivos e faixas de linhas sem cobertura, ordenados
por risco de falha em producao, com uma frase de justificativa.
Ignore codigo trivial e migracoes. Nao escreva testes ainda.
Leitura crítica dos testes gerados
Uma asserção é a afirmação que o teste verifica; é a única parte que realmente distingue sucesso de fracasso. Uma asserção vazia é aquela que passa quase sempre, independentemente do comportamento do código. Ao revisar o diff de testes, procure estes vícios:
- Testes que só verificam que a função não lançou exceção, sem olhar o valor devolvido.
- Comparações do resultado com ele mesmo, ou com uma variável calculada pela mesma lógica que está sendo testada.
- Verificações do tipo "é diferente de nulo" ou "o tamanho é maior que zero" quando o valor exato é conhecido.
- Testes marcados para serem pulados, ou blocos que capturam qualquer exceção e seguem adiante.
- Uso de dublês de teste para tudo, a ponto de o teste verificar apenas que o dublê foi chamado, nunca o efeito real.
- Números mágicos copiados da saída atual do programa sem que ninguém tenha conferido se aquela saída está certa.
Há uma verificação prática que desmascara quase todos eles: quebre o código de propósito. Faça isso apenas com a árvore de trabalho limpa e tudo já commitado, para que desfazer a alteração seja sempre possível. Peça ao agente que introduza temporariamente uma alteração no comportamento, por exemplo inverter um sinal ou trocar um limite, rode a suíte e mostre quais testes falham. Se nenhum falhar, o teste não vale nada. Depois disso, mande desfazer a alteração e confirme com o comando de sessão que mostra o diff que a árvore voltou ao estado anterior.
Testes frágeis
Um teste frágil, também chamado de teste intermitente, é aquele que às vezes passa e às vezes falha sem que o código tenha mudado. Ele corrói a confiança na suíte inteira, porque as pessoas passam a ignorar o vermelho. As causas são quase sempre as mesmas, e cada uma tem uma correção padrão.
| Causa | Sintoma típico | Correção a pedir ao Codex |
|---|---|---|
| Dependência de horário | Falha perto da meia-noite ou na virada do mês | Injetar um relógio controlável e fixar a data no teste |
| Ordem de execução | Falha só quando a suíte roda completa | Isolar estado em fixtures com limpeza garantida |
| Concorrência | Falha rara e não reproduzível | Sincronizar em evento explícito, nunca em pausa fixa |
| Rede ou serviço externo | Falha quando a internet oscila | Substituir por dublê e mover o teste real para outra suíte |
| Dados aleatórios | Falha esporádica com valores estranhos | Fixar a semente do gerador aleatório |
Para investigar, dê ao agente uma tarefa mensurável em vez de uma queixa vaga: "Rode tests/test_relatorio.py cinquenta vezes seguidas, conte quantas vezes falha, identifique a causa da intermitência e proponha uma correção que não seja aumentar tempo de espera." Deixe registrado no AGENTS.md que o uso de pausas fixas para sincronizar testes é proibido. E jamais aceite a solução de marcar o teste como pulado: isso apaga o alarme e mantém o incêndio.
Manutenção da suíte ao longo do tempo
Uma suíte é um ativo que envelhece. Duas rotinas mantêm o custo baixo. A primeira é pedir periodicamente ao agente, em modo somente leitura, um relatório dos testes mais lentos, dos duplicados e daqueles que nunca falharam em nenhuma mudança recente. Cuidado: um teste que nunca falhou não é necessariamente inútil, pode ser exatamente a guarda que impede uma regressão, então avalie caso a caso antes de remover qualquer coisa. A segunda é a regra que você já aplicou ao corrigir bugs: todo defeito encontrado em produção vira um teste de regressão antes da correção. Assim a suíte cresce onde o sistema realmente costuma quebrar, e não onde era mais fácil escrever teste.
Recapitulando
- Registre no AGENTS.md o comando da suíte, o de cobertura e a convenção de nomes, para que o agente sempre execute o que escreveu.
- Peça testes por função, com escopo negativo que proíba alterar código de produção.
- Exija a enumeração de casos de borda, incluindo os duvidosos, antes de qualquer asserção.
- Em testes de integração, especifique o ambiente e exija independência entre testes.
- No ciclo guiado por testes, separe em três pedidos: teste vermelho, implementação mínima sem tocar nos testes, e limpeza.
- Use a cobertura para localizar buracos de risco, nunca como meta numérica.
- Caçe asserções vazias e valide a suíte quebrando o código de propósito.
- Trate testes frágeis pela causa, e nunca os desative.