“Não consigo corrigir este código porque não sei onde ele vai quebrar se eu mexer.”
Todos nós já dissemos isso pelo menos uma vez diante de código legado.
Vamos direto ao ponto. Antes da refatoração, a única coisa a fazer é criar “um teste que capture exatamente o comportamento atual”.
Antes de deixar o código mais bonito, registre exatamente o que ele faz hoje.
Hoje vou explicar a sequência exata que segui ao adicionar testes a um código legado real. Só respeitar a ordem já reduz bastante o risco de quebrar algo enquanto você corrige.
Por que os testes vêm antes da refatoração?
Vamos começar definindo refatoração.
Refatoração é melhorar a estrutura interna sem alterar o comportamento observável.
O ponto central é o compromisso de “não alterar o comportamento”.
Como verificamos esse compromisso? Com testes.
Sem testes, fazemos deploy confiando na sensação de que “parece que não mudou”. Fazer deploy de código de pagamentos no feeling… só de imaginar já dá arrepios.
Por isso Michael Feathers define assim, no livro Working Effectively with Legacy Code: “código legado é código sem testes”.
O critério não é ser antigo ou não, mas ter ou não uma rede de segurança.
Checklist antes da refatoração (resumo principal)
Para quem está com pressa, esta é a ordem.
- Defina primeiro o escopo (limite) do código que será alterado
- Adicione testes de caracterização que registrem o comportamento atual
- Confirme que os testes estão verdes (passando)
- Só então refatore em pequenas unidades
- Execute os testes novamente a cada etapa
Só seguir esses cinco passos na ordem já é meio caminho andado. Vamos detalhá-los abaixo.
Como adicionar testes de caracterização?
Esta é a pergunta que mais recebo: “Não conheço o comportamento; que teste devo escrever?”
É preciso inverter a ideia. Em vez de escrever sabendo a resposta correta, fixe como resposta o resultado atual produzido pelo código.
Isso se chama teste de caracterização (Characterization Test).
O método é surpreendentemente simples. Coloque qualquer valor aproximado e execute o teste. Ele falhará e dirá “este era o valor real”; basta colar esse valor exatamente como está.
// 1) Insira de propósito um valor incorreto porque o valor real de retorno é desconhecido
@Test func cálculo do desconto_comportamento atual() {
let result = calcDiscount(user: user, cart: cart)
#expect(result == 0) // Falhe e mostre o valor real
}
// 2) Fixe o valor real exibido na mensagem de falha (por exemplo: 1500))
// #expect(result == 1500)
Agora este teste se torna o guardião do fato de que “este código originalmente retorna 1500”.
Se um erro durante a refatoração fizer o código retornar 1200, o teste avisará imediatamente com uma luz vermelha.
Ainda não vamos julgar se o código é bom ou ruim. O objetivo é capturar seu estado atual.
O que fazer quando não dá para adicionar testes?
Este é o verdadeiro muro do código legado. Quando coisas intocáveis, como um DB, uma API externa ou o horário atual, estão embutidas no meio de uma função, o teste não roda.
Nessa situação, ajuda o conceito de “seam” apresentado por Feathers: interromper levemente o fluxo para criar um ponto onde valores falsos possam ser inseridos.
A forma mais segura é extrair apenas a linha necessária para um parâmetro da função.
Por exemplo, se a função chama 현재시간() diretamente, basta alterá-la para receber isso como argumento. Assim, o teste pode fornecer o horário desejado.
Um cuidado: até essa mudança mínima feita para adicionar testes deve ser realizada de modo mecânico e cuidadoso. Essa área ainda não tem uma rede de segurança.
Perguntas frequentes (Q&A)
P. Qual percentual de cobertura devo atingir antes de começar?
Você não precisa cobrir tudo. Basta envolver a parte que será alterada agora, esse limite. 100% muitas vezes é uma armadilha, não um objetivo.
P. E se eu não tiver tempo para adicionar testes?
Eu conheço bem essa pressão. Nesse caso, envolva apenas a função que precisa corrigir com um teste de caracterização antes de começar. Cinco minutos evitam o pior incidente.
P. Tudo bem se os testes estiverem bagunçados?
Tudo bem. Testes de caracterização são como um andaime temporário. Quando a refatoração terminar e o código estiver limpo, você também pode organizar os testes.
No fim, tudo se resume a uma sequência: capturar (teste) → alterar (refatoração) → verificar.
Código legado assusta não por ser ruim, mas por não ter uma rede de segurança. Escolha hoje uma única função e adicione um teste de caracterização. Depois disso, tudo fica muito mais fácil. Estou torcendo por você!

