Escrevendo testes para código existente


68

Suponha que um deles tenha um programa relativamente grande (digamos, 900k SLOC em C #), todos comentados / documentados minuciosamente, bem organizados e funcionando bem. Toda a base de código foi escrita por um único desenvolvedor sênior que não está mais na empresa. Todo o código é testável como está e a IoC é usada o tempo todo - exceto por algum motivo estranho, eles não escreveram nenhum teste de unidade. Agora, sua empresa deseja ramificar o código e deseja que testes de unidade sejam adicionados para detectar quando as alterações quebram a funcionalidade principal.

  • A adição de testes é uma boa ideia?
  • Se sim, como alguém começaria algo assim?

EDITAR

OK, então eu não esperava respostas apresentando bons argumentos para conclusões opostas. O problema pode estar fora do meu controle. Também li as "perguntas duplicadas" e o consenso geral é que "escrever testes é bom" ... sim, mas não é muito útil nesse caso em particular.

Acho que não estou sozinho aqui pensando em escrever testes para um sistema legado. Vou manter métricas sobre quanto tempo é gasto e quantas vezes os novos testes detectam problemas (e quantas vezes não). Voltarei e atualizarei isso daqui a um ano ou mais com meus resultados.

CONCLUSÃO

Portanto, é basicamente impossível simplesmente adicionar teste de unidade ao código existente com qualquer aparência de ortodoxia. Uma vez que o código está funcionando, você obviamente não pode colocar luz vermelha / luz verde em seus testes, geralmente não está claro quais comportamentos são importantes para testar, não está claro por onde começar e, certamente, não está claro quando você termina. Realmente, mesmo fazer essa pergunta perde o ponto principal de escrever testes em primeiro lugar. Na maioria dos casos, achei mais fácil reescrever o código usando TDD do que decifrar as funções pretendidas e adicionar retroativamente os testes de unidade. Ao corrigir um problema ou adicionar um novo recurso, é uma história diferente, e acredito que este é o momento de adicionar testes de unidade (como alguns apontaram abaixo). Eventualmente, a maioria dos códigos é reescrita, geralmente mais cedo do que você esperaria.


10
Adicionar testes não quebrará o código existente.
Dan Pichelman

30
@DanPichelman Você nunca experimentou um schroedinbug - "Um bug de design ou implementação em um programa que não se manifesta até que alguém que esteja lendo o código-fonte ou usando o programa de uma maneira incomum perceba que nunca deveria ter funcionado; nesse momento, o programa imediatamente pára de trabalhar para todo mundo até ser consertado ".

8
@ MichaelT Agora que você mencionou, acho que já vi um ou dois deles. Meu comentário deveria ter lido "A adição de testes geralmente não quebra o código existente". Obrigado
Dan Pichelman

3
Escreva apenas testes em torno das coisas que você pretende refatorar ou alterar.
Steven Evers

3
Verifique o livro "Trabalhando efetivamente com o código herdado": amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/… , Michael Feathers sugere escrever os testes antes de modificar qualquer código herdado.
Skarab

Respostas:


68

Embora os testes sejam uma boa idéia, a intenção era que o codificador original os construísse enquanto criava o aplicativo para capturar seu conhecimento de como o código deveria funcionar e o que pode quebrar, o que seria transferido para você.

Ao adotar essa abordagem, há uma alta probabilidade de você escrever os testes com menor probabilidade de quebrar e perder a maioria dos casos extremos que teriam sido descobertos ao criar o aplicativo.

O problema é que a maior parte do valor virá dessas 'pegadinhas' e situações menos óbvias. Sem esses testes, o conjunto de testes perde praticamente toda a sua eficácia. Além disso, a empresa terá uma falsa sensação de segurança em torno de seu aplicativo, pois não será significativamente mais à prova de regressão.

Normalmente, a maneira de lidar com esse tipo de base de código é escrever testes para o novo código e para a refatoração do código antigo até que a base de código herdada seja totalmente refatorada.

Veja também .


11
Mas, mesmo sem TDD, os testes de unidade são úteis ao refatorar.
PDR

11
Se o código continuar funcionando bem, não há problema, mas a melhor coisa a fazer seria testar a interface com o código herdado sempre que você escrever algo que se baseie em seu comportamento.
deworde

11
É por isso que você mede a cobertura do teste . Se o teste para uma seção específica não cobrir todos os ifs e elses e todos os casos extremos, não será possível refatorar com segurança essa seção. A cobertura informará se todas as linhas foram atingidas, portanto seu objetivo é aumentar a cobertura o máximo possível antes da refatoração.
Rudolf Olah

3
Uma grande falha do TDD é que, embora o conjunto possa ser executado, para o desenvolvedor não familiarizado com a base de código, esse é um falso senso de segurança. O BDD é muito melhor nesse aspecto, pois a saída é a intenção do código em inglês simples.
precisa saber é o seguinte

3
Apenas gostaria de mencionar que 100% de cobertura de código não significa que seu código esteja funcionando corretamente 100% do tempo. Você pode ter todas as linhas de código do método testadas, mas apenas porque funciona com o valor1 não significa que é garantido que funcione com o valor2.
Ryanzec

35

Sim, adicionar testes é definitivamente uma boa ideia.

Você diz que está bem documentado, e isso o coloca em uma boa posição. Tente criar testes usando essa documentação como guia, concentrando-se em partes do sistema que são críticas ou estão sujeitas a alterações frequentes.

Inicialmente, o tamanho da base de código provavelmente parecerá impressionante em comparação com a pequena quantidade de testes, mas não existe uma abordagem do big bang, e começar em algum lugar é mais importante do que agonizar sobre qual será a melhor abordagem.

Eu recomendaria o livro de Michael Feathers, Working Effective with Legacy Code , por alguns bons conselhos detalhados.


10
+1. Se você escrever os testes com base na documentação, descobrirá quaisquer discrepâncias entre o código de trabalho e a documentação, e isso é inestimável.
Carl Manaster 6/08/13

11
Outro motivo para adicionar testes: quando um bug é encontrado, você pode adicionar facilmente um caso de teste para futuros testes de regressão.

Essa estratégia é basicamente a descrita no curso on-line (gratuito) do edX CS169.2x no capítulo sobre código legado. Como os professores dizem: "Estabelecendo a verdade básica
FGM

21

Nem todos os testes de unidade têm benefícios iguais. O benefício de um teste de unidade ocorre quando falha. Quanto menor a probabilidade de falhar, menos benéfico é. O código novo ou alterado recentemente tem mais probabilidade de conter erros do que o código raramente alterado que é bem testado na produção. Portanto, é mais provável que testes de unidade em código novo ou alterado recentemente sejam mais benéficos.

Nem todos os testes de unidade têm custo igual. É muito mais fácil testar o código trivial que você projetou hoje do que o código complexo que outra pessoa projetou há muito tempo. Além disso, o teste durante o desenvolvimento geralmente economiza tempo de desenvolvimento. No código legado, a economia de custos não está mais disponível.

Em um mundo ideal, você teria todo o tempo necessário para testar o código legado de unidade, mas no mundo real, em algum momento, é lógico que os custos de adicionar testes de unidade ao código legado superem os benefícios. O truque é identificar esse ponto. Seu controle de versão pode ajudar, mostrando o código alterado mais recentemente e alterado com mais freqüência, e você pode começar colocando-os em teste de unidade. Além disso, quando você fizer alterações no futuro, coloque essas alterações e o código intimamente relacionado em teste de unidade.

Seguindo esse método, eventualmente você terá uma cobertura muito boa nas áreas mais benéficas. Se você passar meses instalando testes de unidade antes de retomar as atividades geradoras de receita, pode ser uma decisão desejável de manutenção de software, mas é uma péssima decisão comercial.


3
Se o código raramente falhar, muito tempo e esforço podem ser gastos para encontrar problemas misteriosos que nunca poderiam ocorrer na vida real. Por outro lado, se o código estiver com erros e propenso a erros, você provavelmente poderá começar a testar em qualquer lugar e encontrar problemas imediatamente.
Robbie Dee

8

A adição de testes é uma boa ideia?

Absolutamente, embora eu ache um pouco difícil acreditar que o código esteja limpo e funcionando bem e usando técnicas modernas e simplesmente não tenha testes de unidade. Tem certeza de que eles não estão em uma solução separada?

De qualquer forma, se você deseja estender / manter o código, os verdadeiros testes de unidade são inestimáveis ​​para esse processo.

Se sim, como alguém começaria algo assim?

Um passo de cada vez. Se você não estiver familiarizado com o teste de unidade, aprenda um pouco. Assim que se familiarizar com os conceitos, escolha uma pequena seção do código e escreva testes para ele. Depois o próximo e o próximo. A cobertura do código pode ajudá-lo a encontrar pontos que você perdeu.

Provavelmente é melhor escolher coisas perigosas / arriscadas / vitais para testar primeiro, mas você pode ser mais eficaz testando algo direto para entrar primeiro em um ritmo - especialmente se você / a equipe não estiver acostumada à base de código e / ou unidade Testando.


7
"Tem certeza de que eles não estão sentados em uma solução separada?" é uma ótima pergunta Espero que o OP não o ignore.
Dan Pichelman

Infelizmente, o aplicativo foi iniciado há muitos anos, assim como o TDD estava ganhando força, então a intenção era fazer testes em algum momento, mas por algum motivo, uma vez iniciado o projeto, eles nunca chegaram a ele.
Paul

2
Eles provavelmente nunca conseguiram, porque levaria um tempo extra que não valia a pena. Um bom desenvolvedor trabalhando sozinho pode definitivamente criar um aplicativo limpo, claro, bem organizado e funcional, de tamanho relativamente grande, sem testes automatizados, e geralmente eles podem fazê-lo mais rapidamente e sem erros do que com os testes. Como tudo está em sua cabeça, há uma chance significativamente menor de erros ou problemas organizacionais em comparação com o caso de vários desenvolvedores que o criaram.
Ben Lee

3

Sim, fazer testes é uma boa ideia. Eles ajudarão a documentar que a base de código existente funciona como pretendido e capturar qualquer comportamento inesperado. Mesmo se os testes falharem inicialmente, deixe-os e refatore o código mais tarde para que eles passem e se comportem como pretendido.

Comece a escrever testes para classes menores (aquelas que não têm dependências e são relativamente simples) e passe para classes maiores (aquelas que têm dependências e são mais complexas). Vai levar muito tempo, mas seja paciente e persistente para que você possa cobrir a base de código o máximo possível.


Você realmente adicionaria testes com falha a um programa que está funcionando bem (o OP diz)?
MarkJ

Sim, porque mostra que algo não está funcionando conforme o esperado e requer uma análise mais aprofundada. Isso levará à discussão e, esperançosamente, corrija quaisquer mal-entendidos ou defeitos anteriormente desconhecidos.
Bernard

@ Bernard - ou, os testes podem expor seu mal-entendido sobre o que o código deve fazer. Testes escritos após o fato correm o risco de não encapsular corretamente as intenções originais.
precisa saber é o seguinte

@ DanPichelman: Concordo, mas isso não deve desencorajar ninguém de escrever nenhum teste.
Bernard

Se nada mais, isso indicaria que o código não foi escrito defensivamente.
Robbie Dee

3

OK, eu vou dar a opinião contrária ....

A adição de testes a um sistema em funcionamento existente alterará esse sistema, a menos que seja, o sistema será todo escrito com zombaria desde o início. Duvido, embora seja bem possível que tenha uma boa separação de todos os componentes com limites facilmente definíveis nos quais você pode inserir suas interfaces simuladas. Mas se não for, você terá que fazer mudanças bastante significativas (relativamente falando) que podem muito bem quebrar as coisas. Na melhor das hipóteses, você gastará muito tempo escrevendo esses testes, tempo que poderia ser melhor gasto escrevendo vários documentos detalhados de design, documentos de análise de impacto ou documentos de configuração da solução. Afinal, esse é o trabalho que seu chefe deseja mais do que testes de unidade. Não é?

Enfim, eu não adicionaria nenhum teste de unidade.

Eu me concentrava em ferramentas de teste automatizadas externas, que oferecem uma cobertura razoável sem alterar nada. Então, quando você vier fazer modificações ... é aí que você poderá começar a adicionar testes de unidade dentro da base de código.


2

Frequentemente, já deparei com essa situação, herdo uma grande base de códigos sem cobertura adequada ou sem cobertura de teste e agora sou responsável por adicionar funcionalidade, corrigir bugs etc.

Meu conselho é garantir e testar o que você adiciona e, se você corrigir bugs ou alterar casos de uso no código atual, o autor testa. Se você precisar tocar em algo, escreva testes nesse ponto.

Quando isso ocorre, quando o código existente não está bem estruturado para testes de unidade, você gasta muito tempo refatorando para poder adicionar testes para pequenas alterações.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.