Como fazer um teste depender dos resultados de outro teste?

13

Digamos que exista uma classe de utilitário que forneça alguns métodos estáticos comuns usados em qualquer parte do código por muitas outras classes.

Como você projetaria seus testes de unidade para os consumidores do utilitário, para que seus testes falhem se algum dos testes do utilitário não passar? Você pode fazer isso ou precisa verificar se os testes da classe de utilidade são todos verdes?

Por exemplo, eu tenho um utilitário de divisão de mensagens que é usado (ou melhor, sua saída) por um analisador de mensagens. Eu gostaria de ter certeza de que o divisor de mensagens funcione corretamente antes que o analisador de mensagens seja testado.

Eu escrevi testes para os dois, mas existe uma maneira de vinculá-los e fazer com que um teste dependa do resultado de outro teste?

Não consegui encontrar uma tag adequada para isso, mas estou usando o mecanismo de teste de unidade do Visual Studio.

t3chb0t
fonte
1
Entendo que o analisador não funcionará corretamente se o utilitário falhar. Mas se você executar seus testes, verá que os testes de utilidade falham. Por que você deseja que outros testes falhem também?
Eugen Martynov
1
Eu nunca ouvi falar de alguém querendo isso antes. O que você está tentando conseguir?
Esben Skov Pedersen
Eu uso algumas das funções do utilitário para preparar dados para os testes do analisador, para ter certeza absoluta de que o utilitário funcione corretamente antes de testar qualquer outra coisa.
T3chb0t
3
@ t3chb0t Se você deseja garantir que seu utilitário funcione, é necessário escrever testes de unidade para verificar seu utilitário. Os testes de unidade devem ser atômicos. Você só deseja que eles testem um único componente.
maple_shaft

Respostas:

11

Não há sentido em garantir que todos os defeitos do seu sistema disparem exatamente um teste.

Um conjunto de testes tem um trabalho: verificar se não há defeitos conhecidos. Se houver um defeito, não importa se um teste falha ou 10. Se você se acostumar com a suíte de testes, se tentar avaliar o quão "ruim" é o seu programa contando os testes com falha, você não estará usando o teste de regressão da maneira certa. O conjunto de testes deve passar em todos os testes antes de publicar o código.

O único motivo válido para ignorar os testes é se eles testam a funcionalidade incompleta e demoram um tempo excessivo que você poderia usar melhor ao implementar o que eles deveriam testar. (Isso é apenas um problema se você não praticar um desenvolvimento estrito orientado a testes, mas é uma escolha válida, afinal).

Caso contrário, não se preocupe em tentar transformar seu conjunto de testes em um indicador que diga exatamente o que há de errado. Nunca será exato, e não deveria ser. É para protegê-lo de cometer o mesmo erro duas vezes, só isso.

Kilian Foth
fonte
8

Depende de suas ferramentas, mas você provavelmente não pode (e não deveria)

Algumas estruturas de teste de unidade (como o PHPUnit, por exemplo) permitem 'encadear' testes para que um teste com falha em um nível não execute outros testes. No entanto, duvido que isso resolva seu problema para esta situação.

Não permitir a execução garantida da ordem de teste força os testes a serem isolados um do outro e é geralmente considerado uma coisa boa. Imagine o que aconteceria se os testes não apenas fossem executados, mas também fornecessem dados para outros testes trabalharem ...

Você pode colocar esses métodos de utilidade em uma solução ou categoria de teste separada. Verifique se eles 'se destacam' visualmente em seu executor de testes ou se essa categoria é executada primeiro e não executa outros testes se os testes nessa categoria falharem *. Quando um desses testes falha, a falha provavelmente se espalhará por todos os testes e você deve ajudar a garantir que quem executa os testes saiba que deve começar corrigindo esses testes com falha. O mesmo vale para testes de unidade e testes de integração. Se um Unittest falhar, ele cairá em cascata e causará todo tipo de confusão nos testes de integração. Todo mundo sabe que, quando os testes de Unidade E Integração falham, você começa verificando os Unittests ...

* Com um script de construção ou teste automatizado, você pode executar esta categoria primeiro, verificar o resultado e executar apenas os outros testes se esse primeiro lote de testes passar.

JDT
fonte
5

Tente manter seu teste de unidade atômico. Lembre-se de que o código de teste automatizado também faz parte da sua base de código, mas não está sendo testado, portanto, mantenha-o o mais simples e óbvio possível.

Para responder sua pergunta mais diretamente, não há garantia da ordem de execução dos testes, portanto, também não há como garantir que seu teste de utilidade seja bem-sucedido antes que seus outros testes sejam executados. Portanto, não há maneira garantida de alcançar o que você deseja enquanto ainda possui um único conjunto de testes para todos os seus testes.

Você pode mover os utilitários para sua própria solução e adicionar uma referência à dll resultante no seu projeto atual.

Torsal
fonte
4

Em um resumo, você não deseja usar ferramentas / técnicas para fazer outras coisas além daquilo a que elas se destinam.

O que você está tentando fazer parece uma preocupação que pode ser resolvida facilmente com uma prática de IC (integração contínua).

Dessa maneira, você pode manter seus testes atômicos, como já sugerido, e deixar o IC cuidar da verificação de seus testes.

Se algum teste falhar, você pode configurá-lo para que ele não permita que seu código seja publicado.

AvetisG
fonte
4

Eu tenho o hábito de sempre tentar diferenciar o código no nível do aplicativo do código no nível da estrutura, por isso encontrei o problema que você está descrevendo com frequência: geralmente você deseja que todo o código no nível da estrutura seja testado antes que qualquer código no nível do aplicativo comece a ser testado. . Além disso, mesmo dentro do código no nível da estrutura, costuma haver alguns módulos de estrutura fundamentais que são usados ​​por todos os outros módulos de estrutura e, se algo falhar nos fundamentos, não há sentido em testar outra coisa.

Infelizmente, os fornecedores de estruturas de teste tendem a ter idéias um pouco rígidas sobre como suas criações devem ser usadas e são bastante protetores dessas idéias, enquanto as pessoas que usam suas estruturas tendem a aceitar o uso pretendido sem questionar. Isso é problemático, porque sufoca a experimentação e a inovação. Não conheço todos os outros, mas prefiro ter a liberdade de tentar fazer algo de uma maneira estranha e ver por mim mesmo se os resultados são melhores ou piores do que o estabelecido, em vez de não ter a liberdade de fazê-lo. faça as coisas do meu jeito em primeiro lugar.

Portanto, na minha opinião, as dependências de teste seriam uma coisa impressionante de se ter, e, em vez disso, a capacidade de especificar a ordem em que os testes serão executados seria a próxima melhor coisa.

A única maneira que encontrei para abordar a questão de ordenar testes é por nomeação cuidadosa, de modo a explorar a tendência de estruturas de teste para executar testes em ordem alfabética.

Não sei como isso funciona no Visual Studio, porque ainda preciso fazer qualquer coisa que envolva testes extensivos com C #, mas no lado Java do mundo funciona da seguinte maneira: Na pasta de origem de um projeto, geralmente temos duas subpastas, um chamado "principal", contendo o código de produção, e um chamado "teste", contendo o código de teste. Em "main", temos uma hierarquia de pastas que corresponde exatamente à hierarquia de pacotes do nosso código-fonte. Os pacotes Java correspondem aproximadamente aos namespaces C #. O C # não exige que você corresponda a hierarquia de pastas à hierarquia de namespace, mas é recomendável fazê-lo.

Agora, o que as pessoas costumam fazer no mundo Java é que, na pasta "test", elas espelham a hierarquia de pastas encontrada na pasta "main", para que cada teste resida exatamente no mesmo pacote da classe que ele testa. A lógica por trás disso é que, com frequência, a classe de teste precisa acessar membros privados da classe em teste, portanto, a classe de teste precisa estar no mesmo pacote que a classe em teste. No lado C # do mundo, não existe visibilidade local para namespaces; portanto, não há razão para espelhar as hierarquias de pastas, mas acho que os programadores de C # seguem mais ou menos a mesma disciplina na estruturação de suas pastas.

De qualquer forma, acho que toda essa idéia de permitir que as classes de teste tenham acesso a membros locais de pacotes das classes sob teste é equivocada, porque eu tendem a testar interfaces, não implementações. Portanto, a hierarquia de pastas dos meus testes não precisa espelhar a hierarquia de pastas do meu código de produção.

Então, o que faço é nomear as pastas (ou seja, os pacotes) dos meus testes da seguinte maneira:

t001_SomeSubsystem
t002_SomeOtherSubsystem
t003_AndYetAnotherSubsystem
...

Isso garante que todos os testes para "SomeSubsystem" serão executados antes de todos os testes para "SomeOtherSubsystem", que por sua vez serão executados antes de todos os testes para "AndYetAnotherSubsystem" e assim por diante.

Dentro de uma pasta, os arquivos de teste individuais são nomeados da seguinte maneira:

T001_ThisTest.java
T002_ThatTest.java
T003_TheOtherTest.java

Obviamente, isso ajuda muito os IDEs modernos a terem poderosos recursos de refatoração que permitem renomear pacotes inteiros (e todos os subpacotes e todo o código que os referencia) com apenas alguns cliques e pressionamentos de tecla.

Mike Nakis
fonte
2
I don't know about everyone else, but I would prefer to have the freedom to try to do something in an odd way, and see for myself whether the results are better or worse++ companheiro. Não sei se gosto da solução, mas gosto da atitude que você demonstrou lá.
precisa saber é o seguinte