Determinando o que é um teste unitário útil

44

Eu tenho lido os documentos do phpunit e vi a seguinte citação:

You can always write more tests. However, you will quickly find that only a fraction of the tests you can imagine are actually useful. What you want is to write tests that fail even though you think they should work, or tests that succeed even though you think they should fail. Another way to think of it is in cost/benefit terms. You want to write tests that will pay you back with information. --Erich Gamma

Isso me fez pensar. Como você determina o que torna um teste de unidade mais útil do que outro, além do que é declarado nessa citação sobre custo / benefício. Como você decide qual parte do seu código você cria testes de unidade? Estou perguntando isso porque outra dessas citações também dizia:

So if it's not about testing, what's it about? It's about figuring out what you are trying to do before you run off half-cocked to try to do it. You write a specification that nails down a small aspect of behaviour in a concise, unambiguous, and executable form. It's that simple. Does that mean you write tests? No. It means you write specifications of what your code will have to do. It means you specify the behaviour of your code ahead of time. But not far ahead of time. In fact, just before you write the code is best because that's when you have as much information at hand as you will up to that point. Like well done TDD, you work in tiny increments... specifying one small aspect of behaviour at a time, then implementing it. When you realize that it's all about specifying behaviour and not writing tests, your point of view shifts. Suddenly the idea of having a Test class for each of your production classes is ridiculously limiting. And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. --Dave Astels

A seção importante disso é

*And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. *

Então, se criar um teste para cada método é 'risível', como e quando você escolheu o que você escreve para os testes?

    
por zcourts 06.07.2011 / 15:33
fonte

4 respostas

25

Quantos testes por método?

Bem, o máximo teórico e altamente impraticável é a complexidade do N-Path (assuma que todos os testes são abrangidos diferentes maneiras através do código;)). O mínimo é UM!. Por método público ou seja, ele não testa detalhes de implementação, apenas comportamentos externos de uma classe (retorna valores e chama outros objetos).

Você cita:

*And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable. *

e depois pergunte:

So if creating a test for each method is 'laughable', how/when do you chose what you write tests for?

Mas acho que você entendeu mal o autor aqui:

A idéia de ter one test method por one method in the class to test é o que o autor chama de "risível".

(Pelo menos para mim) Não é sobre "menos" é sobre "mais"

Então deixe-me reformular como eu o entendi:

E o pensamento de testar cada um dos seus métodos com APENAS UM MÉTODO (o seu próprio método de teste numa relação 1-1) será risível.

Para citar sua citação novamente:

When you realize that it's all about specifying behaviour and not writing tests, your point of view shifts.

Quando você pratica TDD você não pensa :

Eu tenho um método calculateX($a, $b); e ele precisa de um teste testCalculcateX que testa TUDO sobre o método.

O que o TDD lhe diz é pensar em como o seu código DEVE FAZER :

Eu preciso calcular o maior de dois valores ( primeiro caso de teste! ) mas se $ a for menor que zero, então ele deve produzir um erro ( segundo caso de teste! ) e se $ b for menor que zero, deve .... ( terceiro caso de teste! ) e assim por diante.

Você deseja testar comportamentos, não apenas métodos únicos sem contexto.

Dessa forma, você obtém uma suíte de testes que é a documentação do seu código e REALMENTE explica o que se espera, talvez até por que:)

Como você decide qual parte do seu código cria testes de unidade?

Bem, tudo o que acaba no repositório ou em qualquer lugar perto da produção precisa de um teste. Eu não acho que o autor de suas citações discordaria disso, como tentei dizer acima.

Se você não tem um teste para isso, fica muito mais difícil (mais caro) mudar o código, especialmente se você não está fazendo a mudança.

TDD é uma maneira de garantir que você tenha testes para TUDO, mas contanto que você ESCREVA os testes, tudo bem. Geralmente escrevê-los no mesmo dia ajuda, já que você não vai fazer isso mais tarde, está? :)

Resposta aos comentários:

a decent amount of methods can't be tested within a particular context because they either depend or are dependent upon other methods

Bem, existem três coisas que esses métodos podem chamar:

Métodos públicos de outras classes

Podemos ridicularizar outras classes, então definimos o estado lá. Estamos no controle do contexto, então isso não é um problema.

* Métodos protegidos ou privados no mesmo *

Tudo o que não faz parte da API pública de uma classe não é testado diretamente, normalmente.

Você deseja testar o comportamento e não a implementação e, se uma classe fizer tudo o que funciona em um grande método público ou em muitos métodos protegidos menores que forem chamados, será implementação . Você quer ser capaz de MUDAR esses métodos protegidos sem tocar nos seus testes. Porque seus testes vão quebrar se o seu código mudar de comportamento! É para isso que seus testes estão lá, para dizer quando você quebra algo:)

Métodos públicos na mesma classe

Isso não acontece com muita frequência? E se isso acontecer no exemplo a seguir, há algumas maneiras de lidar com isso:

$stuff = new Stuff();
$stuff->setBla(12);
$stuff->setFoo(14);
$stuff->execute(); 

Que os setters existem e não fazem parte da assinatura do método de execução é outro tópico;)

O que podemos testar aqui é se as execuções explodem quando definimos os valores errados. Esse setBla gera uma exceção quando você passa uma string pode ser testado separadamente, mas se quisermos testar se esses dois valores permitidos (12 e 14) não funcionam TOGETHER (por qualquer motivo) do que esse é um caso de teste. / p>

Se você quer um "bom" conjunto de testes, você pode, em php, talvez (!) adicionar uma anotação @covers Stuff::execute para garantir que você só gere cobertura de código para este método e as outras coisas que são apenas configurações precisam ser testado separadamente (novamente, se você quiser isso).

Então o ponto é: Talvez você precise criar alguns dos mundos ao redor primeiro, mas você deve ser capaz de escrever casos de teste significativos que usualmente abrangem apenas uma ou talvez duas funções reais (setters don ' conte aqui). O resto pode ser jogado fora ou ser testado primeiro e depois confiado (veja @depends )

* Nota: A questão foi migrada do SO e inicialmente era sobre PHP / PHPUnit, é por isso que o código de amostra e as referências são do mundo php, eu acho que isso também é aplicável a outros idiomas como phpunit não difere tanto de outras estruturas de testes xUnit.

    
por 06.07.2011 / 16:11
fonte
2

Testes e Testes Unitários não são as mesmas coisas. O Unit Testing é um subconjunto muito importante e interessante do teste geral. Eu diria que o foco do Teste Unitário nos faz pensar sobre esse tipo de teste de maneiras que contradizem um pouco as citações acima.

Primeiro, se seguirmos o TDD ou mesmo o DTH (ou seja, desenvolver e testar em estreita harmonia), estamos usando os testes que escrevemos como foco para acertar o design. Pensando em casos de esquina e escrevendo testes adequadamente, evitamos que os erros entrem em primeiro lugar, então, na verdade, escrevemos testes que esperamos passar (OK, logo no início do TDD eles falham, mas isso é apenas um artefato de pedido, quando o código está pronto, esperamos que eles passem, e a maioria, porque você pensou sobre o código.

Segundo, os Testes de Unidade realmente aparecem quando refatoramos. Nós mudamos nossa implementação, mas esperamos que as respostas permaneçam as mesmas - o Teste de Unidade é nossa proteção contra a quebra do nosso contrato de interface. Então, novamente, esperamos que os testes sejam aprovados.

Isso implica que para nossa interface pública, que presumivelmente é estável, precisamos de uma rastreabilidade clara para que possamos ver que todo método público é testado.

Para responder à sua pergunta explícita: o teste de unidade para interface pública tem valor.

editado no comentário de resposta:

Testando métodos privados? Sim, devemos, mas se não tivermos que testar algo, então é aí que eu comprometo. Afinal, se os métodos públicos estão funcionando, então esses erros podem ser tão importantes em particular ? Pragmaticamente, o churn tende a acontecer nas coisas privadas, você trabalha duro para manter sua interface pública, mas se as coisas de que você depende mudam, o material privado pode mudar. Em algum momento, podemos achar que manter os testes internos é um grande esforço. Esse esforço é bem gasto?

    
por 06.07.2011 / 15:48
fonte
2

Os testes unitários devem fazer parte de uma estratégia de testes maior. Eu sigo estes princípios na escolha de quais tipos de testes para escrever e quando:

  • Concentre-se em escrever testes de ponta a ponta. Você cobre mais código por teste do que com testes de unidade e, assim, obtém mais testes para o investimento. Faça a validação automatizada do sistema como um todo.

  • Drop down para escrever testes unitários em torno de nuggets de lógica complicada. Os testes de unidade valem o seu peso em situações em que os testes de ponta a ponta seriam difíceis de depurar ou difíceis de escrever cobertura de código adequada.

  • Aguarde até que a API em que você está testando esteja estável para gravar qualquer tipo de teste. Você quer evitar refatorar sua implementação e seus testes.

Rob Ashton tem um artigo excelente sobre esse tópico, que eu desenhei com afinco articular os princípios acima.

    
por 26.03.2013 / 12:03
fonte
0

Eu costumo seguir uma abordagem diferente para testes unitários que parece funcionar bem. Em vez de pensar em um teste de unidade como "Testando algum comportamento", penso nele mais como "uma especificação que meu código deve seguir". Desta forma, você pode basicamente declarar que um objeto deve se comportar de uma certa maneira, e dado que você assume que em outro lugar em seu programa, você pode ter certeza de que ele está relativamente livre de erros.

Se você está escrevendo uma API pública, isso é extremamente valioso. No entanto, você sempre precisará de uma boa dose de testes de integração de ponta a ponta, pois a cobertura de 100% dos testes unitários geralmente não vale a pena e perderá as coisas que a maioria consideraria "não testáveis" pelos métodos de teste da unidade. etc)

    
por 26.03.2013 / 19:30
fonte