5 de Abril de 2025
Como Escrever Testes que Não Quebram por Qualquer Detalhe Interno
Como evitar testes frágeis que falham porque você reorganizou código, renomeou método ou mudou uma implementação sem alterar o comportamento real.
Andrews Ribeiro
Founder & Engineer
4 min Intermediario Sistemas
O problema
Tem teste que parece útil até o dia em que você melhora o código.
Você:
- extrai função
- muda ordem interna
- troca uma estrutura de dados
- remove uma chamada intermediária
E a suíte explode.
Não porque o comportamento que importa mudou.
Mas porque o teste estava apegado demais à implementação antiga.
Esse tipo de teste não protege evolução.
Ele pune evolução.
Modelo mental
Pense assim:
teste bom verifica o que o sistema promete. Teste frágil verifica como ele estava fazendo isso ontem.
Essa diferença é tudo.
Se o teste depende de:
- ordem exata de chamadas internas
- número de métodos privados acionados
- estrutura intermediária irrelevante
- detalhes de montagem que o usuário nunca vê
ele deixa de ser rede de segurança e vira alarme falso.
Quebrando o problema
Prefira comportamento observável
A pergunta útil é:
se isso mudar, quem realmente vai sentir?
Boas respostas costumam ser:
- valor retornado
- estado persistido
- evento publicado
- erro exibido
- efeito visível para outra parte do sistema
Más respostas costumam ser:
- quantas funções privadas rodaram
- em que ordem três helpers internos se chamaram
- que variável temporária existia no meio do caminho
Cuidado com espiar tudo
Spy, stub e mock têm lugar.
O problema começa quando o teste usa esses recursos para observar cada passo da coreografia interna.
Aí qualquer refatoração simples vira “regressão”.
O teste continua verde enquanto a estrutura antiga existe.
Mas já não está te ajudando a melhorar nada.
Teste no nível certo da promessa
Se a promessa da função é:
- calcular um total
- validar uma regra
- persistir um registro
- publicar um evento
o teste deve se prender a isso.
Se a promessa não inclui “chamar exatamente estes três métodos nesta ordem”, o teste normalmente também não deveria incluir.
Estrutura interna pode mudar sem o sistema quebrar
Esse é o ponto que muita gente esquece.
Código saudável muda por dentro o tempo todo:
- simplifica
- extrai
- junta
- reorganiza
Se cada mudança interna exige renegociar a suíte inteira, o custo de manutenção sobe demais.
Um teste pode passar e ainda estar piorando o projeto
Esse é um insight importante.
Teste frágil não é só o que falha muito.
É também o que torna o time mais conservador do que deveria:
- ninguém quer refatorar
- ninguém quer limpar nome ruim
- ninguém quer mexer em duplicação
Ou seja, ele degrada o código sem parecer que está causando isso.
Exemplo simples
Imagine uma função que calcula frete.
Teste ruim:
- espiona que
normalizeZipCodefoi chamado - verifica que
getDistanceTablerodou antes deapplyRegionalRule - falha se a implementação interna muda
Teste melhor:
- dado CEP válido e peso X, o frete final é Y
- dado CEP inválido, retorna erro esperado
- dada região promocional, aplica desconto correto
O segundo conjunto protege a regra.
O primeiro protege a coreografia da versão atual.
Erros comuns
- Confundir visibilidade interna com confiança real.
- Escrever teste tão íntimo da implementação que ele quebra em refatoração trivial.
- Verificar chamada de método quando o que importa é o resultado final.
- Acoplar teste a detalhes que o comportamento público não promete.
- Chamar de “rigor” o que na prática é medo de deixar o código mudar.
Como um senior pensa
Quem tem mais experiência costuma olhar para um teste e perguntar:
“Se eu melhorar o código por dentro sem mudar a promessa, esse teste continua fazendo sentido?”
Se a resposta for não, o teste provavelmente está acoplado demais.
Senioridade aqui aparece quando a pessoa protege comportamento e deixa a implementação respirar.
O que o entrevistador quer ver
Em entrevista, esse tema mede maturidade de manutenção, não só conhecimento de framework.
O avaliador quer ver se você:
- diferencia comportamento de implementação
- entende por que teste frágil atrapalha refatoração
- sabe escolher asserts que protegem o que importa
- evita usar spy e mock como muleta para tudo
Uma resposta forte costuma soar assim:
“Eu tento escrever teste no nível da promessa do código. Se uma refatoração interna sem mudança de comportamento quebrar o teste, geralmente é sinal de que o teste sabe demais sobre a implementação.”
Suíte madura não é a que impede o código de mudar. É a que impede o comportamento importante de quebrar.
Quando o teste vigia demais por dentro, ele para de proteger por fora.
Resumo rápido
O que vale manter na cabeça
- Teste robusto observa comportamento relevante, não coreografia interna.
- Quanto mais o teste sabe sobre a implementação, mais caro ele fica para manter.
- Refatoração segura pede testes que deixem a estrutura mudar sem parecer regressão.
- Verificar saída, efeito visível e contrato costuma proteger melhor do que espionar detalhe interno.
Checklist de pratica
Use isto ao responder
- Consigo dizer se um teste está acoplado ao comportamento ou à implementação?
- Sei identificar quando um assert está validando detalhe sem valor real?
- Consigo refatorar código sem ter que reescrever metade da suíte?
- Sei explicar em entrevista por que teste frágil atrasa evolução mesmo “passando” bastante?
Você concluiu este artigo
Compartilhar esta página
Copie o link manualmente no campo abaixo.