Estou escrevendo um analisador e, como parte disso, tenho uma Expander
classe que "expande" uma única instrução complexa em várias instruções simples. Por exemplo, expandiria isso:
x = 2 + 3 * a
para dentro:
tmp1 = 3 * a
x = 2 + tmp1
Agora, estou pensando em como testar esta classe, especificamente em como organizar os testes. Eu poderia criar manualmente a árvore de sintaxe de entrada:
var input = new AssignStatement(
new Variable("x"),
new BinaryExpression(
new Constant(2),
BinaryOperator.Plus,
new BinaryExpression(new Constant(3), BinaryOperator.Multiply, new Variable("a"))));
Ou eu poderia escrever como uma string e analisá-la:
var input = new Parser().ParseStatement("x = 2 + 3 * a");
A segunda opção é muito mais simples, mais curta e legível. Mas também introduz uma denpendência Parser
, o que significa que um erro Parser
pode falhar neste teste. Então, o teste deixaria de ser um teste de unidade de Expander
, e eu acho que tecnicamente se torna um teste de integração de Parser
e Expander
.
Minha pergunta é: é bom confiar principalmente (ou completamente) nesse tipo de teste de integração para testar essa Expander
classe?
Parser
poder falhar em algum outro teste não é um problema se você normalmente comprometer apenas com zero falhas, pelo contrário, significa que você tem mais coberturaParser
. O que eu preferiria me preocupar é que um bugParser
poderia fazer esse teste ser bem - sucedido quando deveria ter falhado . Afinal, os testes de unidade existem para encontrar bugs - um teste é interrompido quando não existe, mas deveria ter.Respostas:
Você se encontrará escrevendo muito mais testes, de comportamento muito mais complicado, interessante e útil, se puder fazê-lo com simplicidade. Então a opção que envolve
é bastante válido. Depende de outro componente. Mas tudo depende de dezenas de outros componentes. Se você zomba de algo dentro de uma polegada de sua vida, provavelmente está dependendo de muitos recursos de zombaria e acessórios de teste.
Às vezes, os desenvolvedores se concentram demais na pureza de seus testes de unidade ou no desenvolvimento de testes de unidade e testes de unidade apenas , sem nenhum módulo, integração, estresse ou outros tipos de testes. Todos esses formulários são válidos e úteis, e são de inteira responsabilidade dos desenvolvedores - não apenas do Q / A ou do pessoal de operações mais adiante.
Uma abordagem que usei é começar com essas execuções de nível mais alto e depois usar os dados produzidos a partir deles para construir a expressão do teste em forma longa e com o menor denominador comum. Por exemplo, quando você despeja a estrutura de dados do
input
produzido acima, pode facilmente construir o:tipo de teste que é testado no nível mais baixo. Dessa forma, você obtém uma mistura interessante: um punhado dos testes primitivos mais básicos (testes de unidade puros), mas não passou uma semana escrevendo testes nesse nível primitivo. Isso fornece o tempo necessário para escrever muitos outros testes atômicos um pouco menos atômicos
Parser
. Resultado final: mais testes, mais cobertura, mais detalhes e outros casos interessantes, melhor código e maior garantia de qualidade.fonte
Claro que está tudo bem!
Você sempre precisa de testes funcionais / de integração que exercitem o caminho completo do código. E o caminho completo do código, neste caso, significa incluir a avaliação do código gerado. Ou seja, você testa que a análise
x = 2 + 3 * a
produz código que se executado coma = 5
será definidox
como17
e se executado coma = -2
será definidox
como-4
.Abaixo disso, você deve fazer testes de unidade para bits menores , desde que isso realmente ajude a depurar o código . Os testes mais refinados que você terá, maior a probabilidade de que qualquer alteração no código também precise alterar o teste, porque a interface interna é alterada. Esse teste tem pouco valor a longo prazo e adiciona trabalho de manutenção. Portanto, há um ponto de retorno decrescente e você deve parar antes dele.
fonte
Os testes de unidade permitem fixar itens específicos pontuais que quebram e onde no código eles quebraram. Portanto, eles são bons para testes de granulação muito fina. Bons testes de unidade ajudarão a diminuir o tempo de depuração.
No entanto, pela minha experiência, os testes de unidade raramente são bons o suficiente para realmente verificar a operação correta. Portanto, os testes de integração também são úteis para verificar uma cadeia ou sequência de operações. Os testes de integração fazem você parte do caminho através de testes funcionais. Como você apontou, devido à complexidade dos testes de integração, é mais difícil encontrar o local específico no código em que o teste é interrompido. Ele também tem um pouco mais de fragilidade, pois falhas em qualquer lugar da cadeia causam falhas no teste. Você ainda terá essa cadeia no código de produção, portanto, testar a cadeia real ainda é útil.
Idealmente, você teria os dois, mas de qualquer forma, geralmente ter um teste automatizado é melhor do que não ter nenhum teste.
fonte
Faça muitos testes no analisador e, à medida que o analisador passa nos testes, salve essas saídas em um arquivo para zombar do analisador e testar o outro componente.
fonte