Qual é o valor da verificação em testes de unidade com falha?

13

Embora existam maneiras de impedir que os testes de unidade sejam executados, qual é o valor de verificar os testes de unidade com falha?

Vou usar um exemplo simples: Case Sensitivity. O código atual faz distinção entre maiúsculas e minúsculas. Uma entrada válida para o método é "Cat" e retornaria uma enumeração de Animal.Cat. No entanto, a funcionalidade desejada do método não deve diferenciar maiúsculas de minúsculas. Portanto, se o método descrito fosse aprovado como "gato", ele poderia retornar algo como Animal.Null em vez de Animal.Cat e o teste de unidade falharia. Embora uma simples alteração de código faça com que isso funcione, um problema mais complexo pode levar semanas para ser corrigido, mas identificar o bug com um teste de unidade pode ser uma tarefa menos complexa.

O aplicativo atualmente em análise possui 4 anos de código que "funciona". No entanto, discussões recentes sobre testes de unidade encontraram falhas no código. Alguns precisam apenas de documentação de implementação explícita (por exemplo, com distinção entre maiúsculas e minúsculas) ou código que não executa o bug com base em como ele é chamado atualmente. Porém, testes de unidade podem ser criados executando cenários específicos que farão com que o erro seja visto e sejam entradas válidas.

Qual é o valor do check-in em testes de unidade que exercitam o bug até que alguém possa corrigir o código?

Este teste de unidade deve ser marcado com ignorar, prioridade, categoria etc., para determinar se uma compilação foi bem-sucedida com base nos testes executados? Eventualmente, o teste de unidade deve ser criado para executar o código assim que alguém o corrigir.

Por um lado, mostra que os erros identificados não foram corrigidos. Por outro lado, pode ser difícil encontrar centenas de testes de unidade com falha aparecendo nos logs e eliminar aqueles que devem falhar versus falhas devido a um check-in de código.

Jim G.
fonte
Essa é uma maneira de aumentar esses números de cobertura de teste.
precisa saber é
Se você já se esforçou para escrever o teste de unidade, por que deseja reescrevê-lo quando decide corrigir o problema? Só porque o check-in não significa que ele precisa ser executado no conjunto. (Você pode criar uma categoria para "Problemas conhecidos" e tratar esses testes como uma lista de pendências / TODO).
Caleb

Respostas:

17

Eu não gosto de unittests quebrados registrados porque produzem ruído desnecessário. Após cada unittest eu teria que verificar todos os problemas com falha (vermelho). Está vermelho porque há um novo problema ou porque existe um antigo aberto para fazer / corrigir. Isso não é bom se houver mais de 20 unittests.

Em vez disso eu uso

  • [Ignore("Reason")]atributo que torna o resultado amarelo ou
  • throw new NotImplementedException()que torna o resultado cinza

Nota: Estou usando o NUnit para .net. Não tenho certeza se o recurso "cinza" existe em outras estruturas mais unittest.

Então, eu gosto do seguinte significado dos resultados dos testes de unidade.

  • verde: tudo acabado
  • cinza: novos recursos planejados que precisam ser executados, mas com baixa prioridade
  • amarelo: bugs ainda não corrigidos. Deve ser corrigido em breve
  • vermelho: novos erros. Deve ser corrigido imediatamente

Tudo, exceto "vermelho", pode ser verificado.

Para responder à pergunta: há mais mal do que valor em fazer check-in "testados com falha vermelha", mas fazer o check-in "testes ignorados em amarelo" ou "testes cinza-NotImplementedYet" pode ser útil como uma lista de tarefas.

k3b
fonte
o problema que vejo com essa abordagem é que os testes ignorados provavelmente nunca serão corrigidos. Você também pode simplesmente remover o código de teste inteira, qual seria a diferença (estou sendo um pouco arrogante aqui)
Lovis
4
will probably never be fixedé uma decisão política se você deseja gastar efford em testes automatizados ou não. Com "testes ignorados", você tem a chance de corrigi-los. Jogando "testes ignorados" longe significa "abandonar testes automatizados por e por até que não haja mais"
k3b
8

Não vou fingir que isso é padrão da indústria, mas verifico os testes quebrados como uma maneira de lembrar a mim ou aos meus outros membros do projeto que ainda há um problema no código ou no próprio teste de unidade.

Suponho que uma coisa a considerar é se suas políticas de desenvolvimento permitem testes com falha sem penalidade. Eu tenho um amigo que trabalha em uma loja que faz desenvolvimento orientado a testes, então eles sempre começam com falhas nos testes ...

Tieson T.
fonte
5
Mas você nunca deve fazer o check-in de um teste com falha, pois o servidor de construção não deve criar um projeto com um teste quebrado.
CaffGeek
@Chad: Construir e testar são duas partes separadas de uma etapa automatizada. A construção garante que tudo seja compilado. O teste garante que o resultado da construção seja válido. Minha interpretação da pergunta não foi "devo verificar o código que não é compilado?" Em vez disso, era "devo verificar em um teste que sei que falhará?"
Unholysampler 15/03/11
1
Eu estava apenas adicionando um ponto a considerar, alguns servidores de construção de integração contínua executam os testes e, se falharem, não serão implantados. Por direito, como se a construção falhasse, o código falha e não há sentido em implantar um produto que se sabe que está quebrado.
CaffGeek
@ Chade: Certo, eu esqueci completamente os servidores de CI. Definitivamente, esse seria um ponto a ser considerado. Também vale esclarecer o que entendemos por testes "quebrados"; eles são simples testes "ruins" ou o teste está falhando porque a API mudou de alguma forma?
Tieson T.
A questão deveria ter sido mais clara. Deve ser o teste O compilará, mas o resultado esperado falhará.
6

Os testes de unidade com falha dão à equipe de desenvolvimento visibilidade do que deve ser feito para estar em conformidade com as especificações acordadas.

Em resumo, os testes de unidade com falha fornecem à equipe uma lista "TODO".

Por esse motivo, os testes de unidade com falha são muito melhores do que nenhum teste de unidade. *
A ausência de testes de unidade deixa a equipe de desenvolvimento no escuro; as especificações devem ser confirmadas repetidamente manualmente .

[* Desde que os testes de unidade realmente testem algo útil.]

Jim G.
fonte
2
Existem maneiras melhores de manter uma lista de tarefas, por exemplo, um quadro branco, um aplicativo de lista de tarefas ou um sistema de rastreamento de problemas. É muito mais fácil usar um conjunto de testes, se você espera que ele sempre seja totalmente aprovado, e qualquer falha no teste que aparecer é um novo problema a ser corrigido imediatamente.
bdsl
6

O objetivo dos testes de unidade é afirmar o comportamento esperado de um sistema, não documentar defeitos. Se usarmos testes de unidade para documentar defeitos, a utilidade deles para afirmar o comportamento esperado será reduzida. A resposta para a pergunta "Por que esse teste falhou?" não é um simples "Oh, algo está quebrado que eu não esperava que estivesse quebrado." Tornou-se desconhecido se a falha no teste é esperada ou inesperada.

Aqui está um parágrafo do começo do capítulo 13 de Trabalhando efetivamente com o código legado :

Testes de unidade automatizados são uma ferramenta muito importante, mas não para encontrar bugs - não diretamente, pelo menos. Em geral, os testes automatizados devem especificar uma meta que gostaríamos de cumprir ou tentar preservar o comportamento que já existe. No fluxo natural do desenvolvimento, testes que especificam se tornam testes que preservam . Você encontrará bugs, mas geralmente não é a primeira vez que um teste é executado. Você encontra erros em execuções posteriores quando altera o comportamento que não esperava.

Matthew Rodatus
fonte
3

Mas os quebrados que identificam erros em um novo projeto, nomeado como tal. Dessa forma, você pode ver que eles DEVEM quebrar ... Como estão sendo corrigidos, eles ficam verdes e são movidos para a suíte de testes normal.

NOTA: Esse projeto teria que ser definido para não ser construído no servidor de compilação, se o servidor de compilação impedir checkins que quebram a compilação (supondo que você defina uma compilação quebrada como aquela na qual todos os testes não serão aprovados)

CaffGeek
fonte
+1, embora não haja resposta para fazer check-in ou não, há um argumento importante: build server
k3b 16/03/11
Prefiro usar um atributo para marcar esse teste em vez de movê-lo para um projeto separado.
CodesInChaos
2

Os testes de unidade devem testar casos de erro, além de casos de sucesso de uma função. Uma função deve rejeitar explicitamente entrada incorreta ou deve ter documentação para explicar que entrada é considerada válida.

Se você tem uma função que não está fazendo nenhuma dessas coisas, é um bug e você deve ter uma maneira de registrar que ela existe. Criar um teste de unidade que demonstre esse problema é uma maneira de fazer isso. Arquivar um ticket de bug é outra opção.

O objetivo do teste de unidade não é ter 100% de sucesso, o objetivo é encontrar e corrigir bugs no código. Não fazer testes é uma maneira fácil de obter 100% de sucesso, mas isso não é muito benéfico para o projeto.

unholysampler
fonte
Woah ... "O objetivo do teste de unidade não é ter 100% de sucesso", você está dizendo que nem todos precisam passar !?
CaffGeek
2
@ Chad: O ponto é que é melhor ter testes que você sabe que falharão, mas estão demonstrando um problema real em vez de não ter o teste apenas para que você possa ter a marca de verificação verde no final da compilação / teste noturno.
Unholysampler 15/03/11
8
@unholysampler, nunca tenha testes interrompidos, a menos que estejam claramente marcados como "deveriam" (por estarem em um projeto diferente). Caso contrário, eles se tornarão ruído e você não saberá quando um teste que deveria estar passando foi interrompido. É completamente derrota o propósito de integração contínua e tendo UnitTests
CaffGeek
2
@ Chad: Eu acho que isso está entrando na semântica das definições. Com base no OP, parecia que ele estava falando sobre a criação de um teste válido que exerce um bug. No entanto, o erro é de baixa prioridade e provavelmente não será corrigido imediatamente. Foi você quem criou a Integração Contínua, que impõe restrições muito mais rígidas ao processo automatizado.
Unholysampler 15/03
4
@unholysampler, IC ou nenhum IC, o ponto é que, quando você executa os testes e está acostumado a ver algumas luzes vermelhas, você se acostuma. Então, quando algo que era verde fica vermelho ... como você sabe?!? É uma prática horrível, e um dos motivos pelos quais os testes não são aceitos em muitas organizações.
CaffGeek
1

Arquive um bug para cada falha e observe isso no resultado do teste. Então, se você agir e corrigir o erro, seu teste será aprovado e você o excluirá do resultado do teste. Nunca apenas ignore problemas.

SnoopDougieDoug
fonte
-3

Como eu vejo o TDD feito com a implementação de testes para um código inacabado, escreva primeiro os testes com o atributo [ExpectedException] ou similar. Isso deve passar inicialmente, pois o código incompleto não teria lógica e escrever um novo código Exception (). Embora a exceção seja incorreta, isso faria com que os testes passassem inicialmente e se adequassem ao check-in. Podemos esquecer um teste ignorado, mas definitivamente podemos arrumar ou preencher o código incompleto. Quando o fazemos, automaticamente o teste correspondente que estava esperando uma exceção agora começaria a falhar e o alertaria para corrigi-lo. Isso pode envolver uma ligeira alteração no teste para se livrar do ExpectException e, em vez disso, fazer afirmações reais. CI, Dev, testadores e clientes, todos felizes e com uma situação ganha-ganha?

user211764
fonte
1
Isso não responde à pergunta. Não está perguntando o que é TDD e por que testar as exceções esperadas.
Andy Wiesendanger