O que você deve testar com testes unitários?

110

Estou recentemente fora da faculdade e começando a universidade em algum lugar na próxima semana. Nós vimos testes unitários, mas nós não os usamos muito; e todo mundo fala sobre eles, então eu achei que talvez devesse fazer alguns.

O problema é que não sei o que testar. Devo testar o caso comum? O caso de borda? Como sei que uma função está adequadamente coberta?

Eu sempre tenho a terrível sensação de que, embora um teste prove que uma função funciona para um determinado caso, é totalmente inútil provar que a função funciona, ponto final.

    
por zneak 03.09.2010 / 18:28
fonte

7 respostas

106

Minha filosofia pessoal tem sido assim:

  1. Teste o caso comum de tudo que você puder. Isso informará quando o código for interrompido depois que você fizer alguma alteração (que, na minha opinião, é o maior benefício do teste unitário automatizado).
  2. Teste os casos de borda de alguns códigos incomumente complexos que provavelmente terão erros.
  3. Sempre que você encontrar um bug, escreva um caso de teste para cobri-lo antes de corrigi-lo
  4. Adicione testes de caso de borda a um código menos crítico sempre que alguém tiver tempo de matar.
por 03.09.2010 / 18:47
fonte
58

Entre a multiplicidade de respostas, portanto, ninguém tocou no particionamento de equivalência e análise do valor limite , considerações vitais na resposta à questão em questão. Todas as outras respostas, embora úteis, são qualitativas, mas é possível - e preferível - ser quantitativo aqui. O @fishtoaster fornece algumas diretrizes concretas, apenas examinando os bastidores da quantificação de testes, mas o particionamento de equivalência e a análise do valor de limite nos permitem fazer melhor.

No particionamento de equivalência , você divide o conjunto de todas as entradas possíveis em grupos com base nos resultados esperados. Qualquer entrada de um grupo produzirá resultados equivalentes, portanto, esses grupos são chamados de classes de equivalência . (Observe que resultados equivalentes não não significam resultados idênticos.)

Como um exemplo simples, considere um programa que deve transformar caracteres ASCII em letras minúsculas em caracteres maiúsculos. Outros caracteres devem passar por uma transformação de identidade, ou seja, permanecem inalterados. Aqui está uma possível divisão em classes de equivalência:

| # |  Equivalence class    | Input        | Output       | # test cases |
+------------------------------------------------------------------------+
| 1 | Lowercase letter      | a - z        | A - Z        | 26           |
| 2 | Uppercase letter      | A - Z        | A - Z        | 26           |
| 3 | Non-alphabetic chars  | [email protected]#,/"... | [email protected]#,/"... | 42           |
| 4 | Non-printable chars   | ^C,^S,TAB... | ^C,^S,TAB... | 34           |

A última coluna relata o número de casos de teste, se você enumerar todos eles. Tecnicamente, pela regra 1 do @ fishtoaster você incluiria 52 casos de teste - todos aqueles para as duas primeiras linhas dadas acima se enquadram no "caso comum". @ A regra 2 do fishtoaster adicionaria algumas ou todas as linhas 3 e 4 acima também. Mas, com o teste de particionamento de equivalência, qualquer caso de teste um em cada classe de equivalência é suficiente. Se você selecionar "a" ou "g" ou "w", estará testando o mesmo caminho de código. Assim, você tem um total de 4 casos de teste em vez de 52 +.

Análise de valor limite recomenda um leve refinamento: essencialmente sugere que nem todo membro de uma classe de equivalência é, bem, equivalente. Ou seja, os valores nos limites também devem ser considerados merecedores de um caso de teste. (Uma justificativa fácil para isso é o infame erro direto !) Assim, para cada equivalência classe você poderia ter 3 entradas de teste. Olhando para o domínio de entrada acima - e com algum conhecimento de valores ASCII - eu poderia apresentar estas entradas de caso de teste:

| # | Input                | # test cases |
| 1 | a, w, z              | 3            |
| 2 | A, E, Z              | 3            |
| 3 | 0, 5, 9, !, @, *, ~  | 7            |
| 4 | nul, esc, space, del | 4            |

(Assim que você obtém mais de 3 valores limites que sugerem que você pode querer repensar seus delineamentos de classe de equivalência original, mas isso foi simples o suficiente para não voltar a revisá-los.) Assim, a análise do valor limite nos traz até apenas 17 casos de teste - com uma alta confiança de cobertura completa - em comparação com 128 casos de teste para realizar testes exaustivos. (Sem mencionar que a combinatória dita que testes exaustivos são simplesmente impossíveis para qualquer aplicação do mundo real!)

    
por 21.11.2013 / 23:32
fonte
17

Provavelmente minha opinião não é muito popular. Mas sugiro que você seja econômico com testes unitários. Se você tiver muitos testes de unidade, poderá acabar gastando metade do seu tempo ou mais com a manutenção de testes, em vez da codificação real.

Eu sugiro que você escreva testes para coisas que você tem um mau pressentimento em seu instinto ou coisas que são muito cruciais e / ou elementares. Os testes unitários IMHO não substituem uma boa engenharia e codificação defensiva. Atualmente trabalho em um projeto que é mais ou menos incomum. É realmente estável, mas é uma dor para refatorar. Na verdade, ninguém tocou este código em um ano e a pilha de software na qual ele se baseia tem 4 anos. Por quê? Porque é confuso com testes de unidade, para ser preciso: testes de unidade e testes de integração automatizados. (Já ouviu falar de pepino e afins?) E aqui está a melhor parte: Este (ainda) inusual software foi desenvolvido por uma empresa cujos funcionários são pioneiros no cenário de desenvolvimento orientado a testes. : D

Então, minha sugestão é:

  • Comece a escrever testes depois de você desenvolver o esqueleto básico, caso contrário a refatoração pode ser dolorosa. Como um desenvolvedor que se desenvolve para outras pessoas, você nunca recebe os requisitos logo no início.

  • Certifique-se de que seus testes de unidade possam ser realizados rapidamente. Se você tiver testes de integração (como o pepino), tudo bem se eles demorarem um pouco mais. Mas testes de longa duração não são divertidos, acredite em mim. (As pessoas esquecem todas as razões pelas quais o C ++ se tornou menos popular ...)

  • Deixe esse material do TDD para os especialistas do TDD.

  • E sim, às vezes você se concentra nos casos de limite, às vezes nos casos comuns, dependendo de onde você espera o inesperado. No entanto, se você sempre espera o inesperado, deve realmente repensar seu fluxo de trabalho e disciplina. ; -)

por 21.08.2011 / 15:49
fonte
8

Se você estiver testando primeiro com Desenvolvimento Orientado a Testes, sua cobertura será de 90% ou mais, porque você não adicionará funcionalidade sem antes escrever um teste de unidade com falha.

Se você estiver adicionando testes após o fato, não posso recomendar o suficiente para obter uma cópia do Trabalhando efetivamente com o código herdado por Michael Feathers e dê uma olhada em algumas das técnicas para adicionar testes ao seu código e maneiras de refatorar seu código para torná-lo mais testável.

    
por 03.09.2010 / 20:00
fonte
5

Se você começar a seguir as práticas de Desenvolvimento orientado a testes , eles classificarão guia você através do processo e saber o que testar virá naturalmente. Alguns lugares para começar:

Os testes vêm em primeiro lugar

Nunca, nunca escreva código antes de escrever os testes. Veja Red-Green-Refactor-Repeat para uma explicação.

Escrever testes de regressão

Sempre que você encontrar um bug, escreva um testcase e verifique se ele falha . A menos que você consiga reproduzir um bug por meio de um testcase com falha, você realmente não o encontrou.

Repetição do Refator Vermelho-Verde

Red : Comece escrevendo um teste básico para o comportamento que você está tentando implementar. Pense nessa etapa a partir da criação de um código de exemplo que usa a classe ou a função em que você está trabalhando. Verifique se ele compila / não tem erros de sintaxe e se ele falha . Isso deve ser óbvio: você não escreveu nenhum código, então deve falhar, certo? O importante a aprender aqui é que, a menos que você veja o teste falhar pelo menos uma vez, nunca poderá ter certeza de que, se ele passar, ele o fará por causa de algo que você fez por causa de alguma razão falsa.

Green : Escreva o código mais simples e estúpido que realmente faz o teste passar. Não tente ser inteligente. Mesmo que você veja que há um caso de borda óbvio, mas o teste leva em conta, não escreva código para lidar com ele (mas não se esqueça do caso de borda: você precisará dele mais tarde) . A idéia é que cada pedaço de código que você escreve, todo if , todo try: ... except: ... deve ser justificado por um caso de teste. O código não precisa ser elegante, rápido ou otimizado. Você só quer que o teste passe.

Refator : Limpe seu código, obtenha os nomes dos métodos corretamente. Veja se o teste ainda está passando. Otimizar. Execute o teste novamente.

Repita : Você se lembra do caso de borda que o teste não cobriu, certo? Então, agora é o seu grande momento. Escreva um testcase que cubra essa situação, observe-o falhar, escreva algum código, veja-o passar, refatorar.

Teste seu código

Você está trabalhando em uma parte específica do código e é exatamente isso que deseja testar. Isso significa que você não deve testar as funções da biblioteca, a biblioteca padrão ou seu compilador. Além disso, tente evitar testar o "mundo". Isso inclui: chamar APIs da web externas, algum material intensivo de banco de dados, etc. Sempre que você tentar fazer uma simulação (faça um objeto que siga a mesma interface, mas retorne dados estáticos e predefinidos).

    
por 13.10.2010 / 02:08
fonte
3

Para testes de unidade, comece com o teste de que ele faz o que foi projetado para fazer. Esse deve ser o primeiro caso que você escreve. Se parte do design for "ele deve lançar uma exceção se você passar em lixo", teste isso também, já que isso faz parte do design.

Comece com isso. À medida que você tiver experiência em fazer o teste mais básico, começará a aprender se é suficiente ou não e começará a ver outros aspectos do seu código que precisam ser testados.

    
por 22.08.2011 / 00:25
fonte
0

A resposta das ações é a "testar tudo o que poderia quebrar" .

O que é muito simples de quebrar? Campos de dados, acessadores de propriedade com morte cerebral e sobrecarga de texto padronizado semelhante. Qualquer outra coisa provavelmente implementa alguma parte identificável de um requisito e pode se beneficiar do teste.

É claro que sua milhagem e as práticas do ambiente de trabalho podem variar.

    
por 25.09.2010 / 00:01
fonte