Práticas recomendadas para a atualização de código legado com testes automatizados

22

Estou prestes a reimplementar uma interface já definida (um conjunto de arquivos de cabeçalho C ++) em uma base de código antiga e relativamente grande. Antes de fazer isso, gostaria de ter a cobertura de teste o mais completa possível, para poder detectar erros de reimplementação o mais cedo e fácil possível. O problema é que a base de código já existente não foi projetada para ser facilmente testável, com (muito) grandes classes e funções, um alto grau de acoplamento, funções com (muitos) efeitos colaterais etc.

Seria bom saber de qualquer experiência anterior com tarefas semelhantes e algumas dicas boas e concretas sobre como você fez a adaptação de testes automatizados (unidade, integrações, regressão etc.) ao seu código legado.

tjansson
fonte
1
Etapa 1: Pesquisar estouro de pilha. A pergunta foi feita. Muitas, muitas vezes.
precisa saber é o seguinte

Respostas:

20

Antes de tudo, obtenha e leia Trabalhando com código legado de Michael Feathers - é uma ajuda indispensável para essas tarefas.

Depois, algumas notas:

  • você tem uma especificação / contrato preciso para a interface ou praticamente só possui a implementação existente como "especificação"? No primeiro caso, é mais fácil fazer uma reescrita completa do zero; no último, é difícil ou impossível.
  • se você deseja reimplementar a interface, a maneira mais útil de gastar seus recursos de teste é escrever testes somente na interface. Obviamente, isso não se qualifica como teste de unidade no sentido estrito, mas sim funcional / de aceitação, mas não sou purista :-) No entanto, esses testes são reutilizáveis ​​e permitem comparar diretamente os resultados das duas implementações lado a lado. .
  • No geral, eu preferiria refatorar o código existente em vez de reescrevê-lo do zero, a menos que seja completamente impossível de manter. (Mas, neste caso, como você vai escrever testes de unidade contra isso?) Confira este post de Joel para uma discussão mais detalhada sobre o assunto. A criação de um conjunto de testes de aceitação contra a interface fornece uma rede de segurança fina, porém útil, contra a qual você pode começar a refatorar cautelosamente o código existente para torná-lo testável por unidade (usando as idéias do livro de Feathers).
Péter Török
fonte
Eu faria isso com +3 se pudesse. Welc é uma leitura essencial e definitivamente ir para um refactoring ...
Johnsyweb
Um pequeno comentário sobre o segundo ponto é que, para sistemas legados, o teste deve ser realizado de acordo com a mentalidade do teste de caracterização . Ou seja, capture fielmente o comportamento atual do software e evite alterar o comportamento, mesmo que alguns dos resultados do teste pareçam estranhos ou não sejam agradáveis ​​de acordo com a mentalidade do teste de unidade. (Btw essa ideia também vem do autor de welc.)
rwong
@ rwong, de fato. Sem uma especificação detalhada, ou um proprietário do produto com conhecimento, é impossível para o desenvolvedor decidir se um comportamento específico do programa a) é intencional e necessário, b) não é intencional, mas agora os usuários dependem dele, c) um bug que na verdade prejudica os usuários, d) um bug totalmente despercebido até agora. Nos dois primeiros casos, "consertar" realmente machucaria os usuários e, no último caso, a correção - embora teoricamente correta - não proporcionaria nenhum benefício visível.
Péter Török
4

O melhor método é saber é o método Mikado. http://mikadomethod.wordpress.com/2010/08/04/the-mikado-method-book/ Esta é apenas a generalização de uma técnica simples, mas é a única maneira que eu sei para começar a melhorar a qualidade do código em uma grande base de código sem correr riscos desnecessários.

O WEWLC também é um livro muito bom sobre o assunto, mas ser escrito em C ++ nem sempre é útil com código Java ou Ruby.

Uberto
fonte
2

Testes de adaptação retro em uma base de código antiga podem ser bastante difíceis se forem monolíticos no design.

Se possível (você tem tempo / dinheiro), uma maneira de avançar seria refatorar o código em unidades mais testáveis.

ozz
fonte
1

Eu gostaria de adicionar um link . Existem poucos exemplos de implementações não tão facilmente testáveis ​​re-fatoradas em códigos mais compatíveis com xUnit. Quanto à abordagem geral, tente os links já mencionados (postagem de Joel, código Working With Legacy

yoosiba
fonte