Como lidar com um número tremendo de testes com falha? [fechadas]

22

Estou trabalhando no desenvolvimento de um projeto antigo escrito em Java. Temos mais de 10 milhões de LOC e, pior ainda, mais de 4000 testes funcionais.

Os testes, programados por Hudson, estão falhando loucamente a cada mudança maior de código. A verificação da falha do teste - se houver um problema no produto ou no teste, leva meses. Não podemos remover os testes antigos porque não sabemos o que eles estão testando!

O que podemos fazer? Como proceder com essa quantidade de testes herdados?

Hector Brosuli
fonte
6
Perguntas reais têm respostas. Em vez de explicar por que sua situação é terrível ou por que seu chefe / colega de trabalho deixa você infeliz, explique o que você quer fazer para melhorar. Para obter mais informações, clique aqui ...
gnat
13
Por que você permitiu que os testes começassem a falhar em primeiro lugar? Entre 4000 não é que muitos testes para 10 MLOC
BЈовић
6
Pare, solte e role.
Navin
13
Descubra o que os testes estão testando. Depois, revise e pergunte antes de tudo como os testes na terra levam meses para encontrar um problema e também descubra como seus requisitos mudaram tanto. Testes destinam-se a encapsular requisitos em um aplicativo. Se seus testes falharem, seu código não está executando de acordo com os requisitos - você os escreveu incorretamente ou nenhum dos seus códigos cumpre seus requisitos.
Dan Pantry
6
Todos nós vimos um compilador lançar um zilhão de erros por causa de um único '}' ausente. Se estes são testes funcionais com uma infinidade de dependências, talvez o mesmo tipo de problema esteja em funcionamento?
Dan Pichelman

Respostas:

37

Abandone-os.

Sei que é difícil deixar de lado algo que foi claramente muito esforço para produzir, mas os testes não estão funcionando para você, estão trabalhando contra você. Um conjunto de testes deve dar a você confiança de que o sistema faz o que deve fazer. Se não fizer isso, é um passivo em vez de um ativo. Não importa se o sistema ou os testes estão com defeito - desde que a execução do conjunto de testes sinalize grandes quantidades de erros, ele não pode cumprir seu objetivo.

O que você precisa agora é de um novo conjunto de testes que é executado sem erros. Isso significa que inicialmente terá pouca cobertura, na verdade quase nenhuma cobertura. Sempre que você conserta ou dedica um tempo para entender completamente algo sobre seu sistema, você reordena esse conhecimento em um teste. Com o tempo, isso produzirá uma nova rede de segurança na qual você poderá construir no futuro. Tentar consertar uma rede de segurança antiga e mal compreendida é um tempo que quase nunca vale a pena.

Eu até defendia a transferência de testes do antigo pacote para o novo. Claro, alguns deles podem ter sucesso agora, mas é porque estão testando exatamente o que deveriam ou apenas porque alguns tiros aleatórios sempre atingem o alvo? Obviamente, você precisa ser pragmático sobre o que pode e o que não pode ser feito com o esforço disponível, mas não pode se comprometer com o princípio de que um conjunto de testes deve ser executado de maneira limpa para realizar seu trabalho .

Kilian Foth
fonte
9
Não consigo entender a lógica do seu argumento: "Um conjunto de testes deve dar a você confiança de que o sistema faz o que deve fazer. [...] O que você precisa agora é de um novo conjunto de testes que roda sem erros ". Se você tiver um código defeituoso que faz com que os testes falhem, isso não significa que você deve reescrever os testes para que o código defeituoso seja aprovado.
precisa saber é o seguinte
13
A situação de Hector é que ele não sabe se o código ou os testes estão errados . Se o fizesse, ele poderia trabalhar com a base de código e alterar algumas vezes os testes, às vezes o código comercial. Mesmo assim, mesmo esse tipo de trabalho não valeria a pena, pois você não sabe se está corrigindo problemas ou perpetuando-os.
Kilian Foth
5
"Um conjunto de testes deve dar a você confiança de que o sistema faz o que deveria". Não, deveria me dizer se o sistema faz o que deveria; a falsa confiança é pior que nenhuma. "O que você precisa é de uma suíte de testes executada sem erros" Não, o que ele precisa é de uma suíte de testes que forneça informações úteis sobre a integridade do código. O que ele tem agora são muitas luzes de advertência enigmáticas, melhores do que uma luz verde de um novo conjunto de testes brilhante que não testa nada. Ele deve desativar temporariamente os testes antigos , mas não abandonar nenhum que não tenha confirmado como falso.
Beta
4
Esta resposta é um conselho incrivelmente ruim! Se alterações menores no código levarem a uma grande quantidade de testes com falha, você provavelmente terá problemas de qualidade do código. O teste notificará você pelo menos que você quebrou alguma coisa. Você precisa melhorar o código (refatorando cuidadosamente auxiliado por testes). Se você acabou de remover os testes, não tem como saber se quebra alguma coisa.
JacquesB
4
Este é um conselho terrível. Se o OP e sua equipe já não conseguem entender a base de código e seus testes, é improvável que jogar fora os testes e recomeçar resolva o problema principal do OP - entender a base de código. Acho que podemos assumir que os testes funcionaram quando escritos - então, sua equipe precisa rastrear o que cada teste está testando e ler a fonte para determinar se é a base de código ou o teste que está errado hoje. Muito mais simples do que recomeçar com testes desorientados e desinformados / ingênuos.
SnakeDoc 17/09/2015
29

Vá e corrija os testes.

Seu maior erro é que você permitiu que os testes falhassem e obviamente ignorou por um tempo. O que você tem não são "testes herdados" - você está trabalhando em um código herdado. E considero todo código escrito sem testes como legado.


A verificação da falha do teste - se houver problema no produto ou no teste, leva meses. Não podemos remover os testes antigos porque não sabíamos o que eles estavam testando!

Parece que há um problema ainda maior em sua organização, pois você não está trabalhando com requisitos claros. Não consigo entender que você (ou outra pessoa) não pode confirmar o comportamento correto.

BЈовић
fonte
4
É o que idealmente deve ser feito, mas parece que os testes aqui são tão ruins que os programadores nem sabem o que estão testando. Penso que, neste caso, pode ser melhor livrar-se dos testes WTF e começar a escrever testes novos e significativos imediatamente! Em um projeto recente, tive um problema semelhante com um colega de trabalho cujos testes sempre falhavam sem boas razões (não falhava porque o que deveria ser testado errava, mas porque o código do teste era tão quebradiço e nem determinístico!) . Passei dias reescrevendo o que pude e joguei fora o resto!
Shautieh 17/09/2015
@ Shautieh Os testes WTF não passam sem o código WTF, portanto, corrigir testes geralmente significa refatorar o código. E testes que falham aleatoriamente são o sinal de incompetência. E o supervisor do seu colega é o responsável por não fazer o trabalho deles.
BЈовић
2
Às vezes a vida é dura: o responsável pelos testes (e código) da WTF ganhou o salário mais alto da equipe (20% a mais do que eu) e, quando desistiu no meio do projeto (porque encontrou um emprego mais bem remunerado) ) eu tinha que tomar sobre alguns de seus devs: / Mas você está absolutamente certo ao dizer que o nosso supervisor era o culpado também ^^
Shautieh
@ Shautieh: um colega meu disse uma vez que um bug no código é dois bugs: um bug no código e um ponto cego nos testes. Acho que na verdade são três se você contar com o desenvolvedor que tolera falhas nos testes e quatro se você contar com os gerentes que promovem esse tipo de incompetência.
Beta
@Beta Parece bastante semelhante à definição usada no TDD: "Um bug é um teste que você ainda não escreveu".
Restabeleça Monica
22

Os testes são valiosos. No mínimo, eles registram que alguém considerou que deveria gastar tempo escrevendo-os; portanto, presumivelmente, eles tiveram algum valor para alguém uma vez. Com sorte, eles conterão um registro completo de todos os recursos e bugs em que a equipe já trabalhou - embora também possam ter sido uma maneira de atingir um número arbitrário de cobertura de teste sem ser cuidadosamente pensado. Até você olhar para eles, você não saberá qual é o caso aqui.

Se a maioria dos seus testes passar na maior parte do tempo, apenas dê uma olhada e invista o tempo em descobrir o que os poucos testes que falharam estavam tentando fazer e corrigi-los ou melhorá-los para que o trabalho seja mais fácil na próxima vez. Nesse caso, pule para a seção Determinar a intenção de cada teste , para obter alguns conselhos sobre o que fazer com um pequeno número de testes com falha.

Por outro lado, você pode se deparar com uma compilação vermelha agora, e centenas ou até milhares de testes que não passaram por um tempo, e Jenkins não é verde há muito tempo. Nesse momento, o status de criação do Jenkins se tornou inútil e um indicador importante dos problemas com seu check-in não está mais funcionando. Você precisa corrigir isso, mas não pode parar todo o progresso enquanto arruma a bagunça na sua sala de estar.

Para manter sua sanidade mental enquanto executa a arqueologia necessária para determinar qual valor pode ser recuperado dos testes que falharam, recomendo as seguintes etapas:

Desative temporariamente os testes com falha.

Existem várias maneiras de fazer isso, dependendo do seu ambiente, que você não descreve claramente, por isso não posso recomendar nenhuma.

Algumas estruturas suportam a noção de falhas esperadas. Se o seu for o caso, isso é ótimo, pois você verá uma contagem regressiva de quantos testes restam nesta categoria e será informado se alguns deles começarem a passar inesperadamente.

Algumas estruturas oferecem suporte a grupos de testes e permitem que você informe ao Hudson apenas para executar alguns dos testes ou ignorar um grupo de testes. Isso significa que você pode ocasionalmente executar o grupo de teste manualmente para ver se algum deles está passando.

Algumas estruturas permitem que você anote ou marque testes únicos para serem ignorados. É mais difícil administrá-los como um grupo nesse caso, mas impede que eles o distraiam.

Você pode mover os testes para uma árvore de origem que normalmente não está incluída na compilação.

In extremis, você pode excluir o código do HEAD do sistema de controle de versão, mas isso tornará mais difícil reconhecer quando a terceira fase for concluída.

O objetivo é fazer com que Jenkins fique verde o mais rápido possível, para que você possa começar a se mover na direção certa o mais rápido possível.

Mantenha os testes relevantes.

Resolva para adicionar novos testes ao adicionar ou modificar o código e comprometa-se a manter todos os testes aprovados.

Os testes podem falhar por vários motivos, incluindo o fato de que não eram testes bem escritos para começar. Mas uma vez que você tenha Jenkins verde, mantê-lo assim é realmente importante.

Acostume-se a escrever bons testes e faça disso um grande negócio se os testes começarem a falhar.

Determine a intenção de cada teste.

Passe pelos testes desativados, um por um. Comece com os que afetam os módulos que você altera com mais frequência. Determine a intenção do teste e o motivo da falha.

  • Ele testa um recurso que foi removido da base de código de propósito? Então você provavelmente pode excluí-lo.

  • Está pegando um bug que ninguém notou ainda? Restabeleça o teste e corrija o erro.

  • Está falhando porque estava fazendo suposições injustificadas (por exemplo, assumindo que o texto do botão sempre estaria em inglês, mas agora você localizou seu aplicativo para vários idiomas)? Depois, descubra como fazer o teste se concentrar em uma única coisa e isolá-lo das alterações não relacionadas da melhor maneira possível.

  • O teste se estende por todo o aplicativo e representa um teste do sistema? Em seguida, remova-o do seu conjunto principal de testes Jenkins e adicione-o ao conjunto Regression que é executado com menos frequência.

  • A arquitetura do aplicativo mudou além do reconhecimento, para que o teste não faça mais nada útil? Delete isso.

  • O teste foi adicionado para aumentar artificialmente as estatísticas de cobertura do código, mas na verdade nada mais é do que confirmar que o código é compilado corretamente e não entra em um loop infinito? Ou então, o teste simplesmente confirma que a estrutura de simulação selecionada retorna os resultados que você acabou de informar? Delete isso.

Como resultado disso, alguns testes permanecerão, alguns serão modificados, outros serão divididos em vários pedaços independentes e pequenos, e alguns serão removidos. Enquanto você ainda estiver progredindo com novos requisitos, reservar um pouco de tempo para lidar com dívidas técnicas como essa é a responsabilidade.

Bill Michell
fonte
1
É realmente uma péssima idéia desativar os testes apenas porque eles falham! O restante do seu conselho é bom, mas não é esse. Os testes que você não entende nunca devem ser desativados. O objetivo do teste não é obter uma barra verde, o objetivo é obter software funcionando!
precisa saber é o seguinte
Depende da escala do problema. Mas concordo, na verdade não deixei isso claro.
Bill Michell
Adicionado um parágrafo para diferenciar entre "Estamos verde, mas cada mudança faz coisas Go Red" e "temos sido vermelho de tanto tempo, nós esquecemos o que parece verde como"
Bill Michell
Em vez de desativar ou mesmo excluir o teste, algumas estruturas também fornecem a noção de uma falha esperada . Isso pode ajudar a aumentar o SNR, porque você será alertado mais diretamente sobre novas falhas (o que não ocorrerá se sempre houver um grande número de falhas), mas ainda será notificado sobre as falhas conhecidas e - talvez ainda mais importante - quando um o teste que falhou anteriormente passa repentinamente. Se as falhas inesperadas forem lidas e as falhas esperadas ficarem alaranjadas, faça com que os testes vermelhos sejam verdes como os primeiros e os laranja com sua segunda prioridade.
precisa saber é o seguinte
11

4000 testes são um problema intratável. 40 testes são mais tratáveis. Selecione aleatoriamente um número gerenciável de testes para executar e analisar. Classifique os resultados como:

  1. Teste inútil
  2. Teste útil que funciona limpo
  3. Teste útil que falha

Se muitos testes se enquadram na primeira categoria, talvez seja hora de lançar seu conjunto de testes atual e reunir um útil para o código atual.

Se muitos dos testes estão falhando de uma maneira que informa sobre um problema no seu código, você precisa trabalhar com os testes com falha corrigindo as coisas. Você pode achar que a correção de um ou dois erros gera um grande número de testes.

Patricia Shanahan
fonte
2
+ (int) (PI / 3) por fornecer uma maneira real e simples de testar o conjunto de testes - embora eu concorde que, como regra geral, testes como os descritos pelo OP são um sinal de design defeituoso - mas sem testes o que há de errado, qualquer conselho sobre o próprio conjunto de testes (seja "abandoná-los", "corrigir os testes", "escrever novos testes") é simplesmente inútil. Exatamente como você diz: se eu fizesse testes de 4k e para 40 completamente aleatórios desses 3/4 fossem ruins e inúteis - eu não hesitaria em despejar toda a suíte. Se 3/4 deles realmente fosse útil - eu os deixaria e me focaria em melhorar o código.
vaxquis
7

Se esta afirmação for verdadeira,

Os testes ... estão falhando loucamente a cada mudança maior de código.

isso implica que, se você reverter para o código imediatamente antes de uma "alteração maior do código", muitos dos testes serão aprovados novamente. Depois de fazer isso, pegue um pedaço menor de alterações e veja quais testes estão falhando recentemente. Isso o ajudará a isolar melhor quais alterações de código estão causando a falha de quais testes. Para cada teste, depois de isolar o problema, você poderá determinar se o novo código foi defeituoso ou se o teste foi. Se houver algum problema com o novo código, compare-o com a versão mais recente, caso esse bug específico já tenha sido corrigido.

Repita até que você tenha a base de código mais recente.

Isso pode parecer uma tarefa esmagadora, mas é muito provável que quando você seguir esse caminho e começar a isolar alguns dos problemas, um padrão começará a surgir, o que pode acelerar bastante o processo. Por exemplo:

  • Você pode perceber que muitos testes dependem de algo que é defeituoso. Fixar essa peça pode consertar muitos testes.
  • Você pode perceber que muitos testes são falhos e precisam ser corrigidos ou removidos.
  • Você pode perceber que um desenvolvedor em particular tem uma frequência muito maior de causar a quebra de testes. Esse desenvolvedor pode precisar de mais treinamento ou supervisão.
TTT
fonte
3

Se você não souber o que eles estão testando, remova-os até saber. Testes são coisas fluidas; se você remover um recurso que não é mais necessário, deverá esperar alterar o teste que testa esse recurso! Portanto, a menos que você saiba o que os testes estão testando, não terá esperança de alterar a base de código com eles.

Você pode configurar o sistema de teste nas máquinas dos desenvolvedores e executá-lo para que os desenvolvedores possam ver com quais partes os testes estão interagindo, espero fornecer essa documentação ausente e familiarizar-se com a base de código que você não está mudando corretamente ou não mais testes corretamente.

Em resumo - se seus testes antigos falharem quando você faz alterações, as alterações no código não são boas. Use esses testes como um meio de educação sobre como o sistema funciona.

gbjbaanb
fonte
1
É por isso que eu gosto da @Ignoreanotação do JUnit - você pode manter seus testes, mas não executá-los. Então é simplesmente uma questão de reativá-los e corrigi-los um de cada vez. Ele permite que você restrinja seu foco a apenas alguns testes de cada vez, em vez de ficar sobrecarregado com milhares de falhas.
TMN
1
Este é um mau conselho. Você não deve remover ou desativar um teste que não entende. Só se fazer entender o teste, e você está confiante de que testa um recurso obsoleto, ele deve ser desativado ou removido.
precisa saber é o seguinte
2

A coisa mais importante que eu faria é voltar aos fundamentos do que os testes devem fazer e o que a empresa precisa para continuar em movimento. O trabalho do teste é identificar problemas antes que eles se tornem caros para corrigir posteriormente. Eu acho que a palavra-chave nessa frase é "cara". Esses problemas precisam de uma solução comercial. Problemas caros estão aparecendo em campo? Nesse caso, o teste está falhando completamente.

Sua gerência e você precisam chegar a uma verificação da realidade. Você está percebendo que os custos de desenvolvimento estão subindo rapidamente devido a um conjunto de testes herdados. Como esses custos se comparam aos custos de entrega de um produto com defeito porque você desativou os testes? Como eles se comparam à tarefa onerosa de realmente descobrir quais comportamentos os usuários precisam (quais são as coisas que devem ser testadas)?

Esses são problemas que precisam de soluções de negócios porque afetam o lado comercial do trabalho. Você está entregando produtos a um cliente, e esse é um limite pelo qual os negócios estão muito interessados. Eles podem ser capazes de identificar soluções que você, como desenvolvedor, não pode. Por exemplo, pode ser razoável que eles forneçam dois produtos: um produto "legado" para aqueles que precisam de confiabilidade e estão dispostos a renunciar a novos recursos, com um produto "visionário" que pode ter mais falhas, mas é pioneiro. Isso daria a você a oportunidade de desenvolver dois conjuntos independentes de testes ... um herdado com 4000 e outro com mais testes que você acha que precisam ser feitos (e documentá-los para que esse processo não se repita).

Então, a arte começa: como você pode gerenciar esse animal de duas cabeças para que os avanços em um ramo também ajudem o outro ramo? Como as atualizações da ramificação "visonary" podem voltar para a ramificação "legada", apesar dos rígidos requisitos de teste. Como as solicitações contínuas de clientes na filial "herdada" moldam melhor sua compreensão dos requisitos que seus clientes herdados precisariam se você re-mesclasse os produtos?

Cort Ammon - Restabelecer Monica
fonte
-3

Não podemos remover os testes antigos porque não sabíamos o que eles estavam testando!

É exatamente por isso que você deve remover os testes antigos! Se você não sabe o que eles estão fazendo, o fracasso não faz sentido e executá-los é uma perda de tempo. Jogue-os fora e comece de novo.

Mohair
fonte
2
este parece meramente ponto de repetição já feito e explicado em resposta superior
mosquito
4
O fracasso não é "sem sentido", significa que você não entende o sistema tão bem quanto imaginou.
Ben Voigt
O fracasso é definitivamente sem sentido aqui, porque o OP afirmou claramente que não entende o sistema.
Mohair