É normal gastar tanto, se não mais, tempo escrevendo testes do que o código real?

203

Acho muito mais difíceis e difíceis de escrever testes do que o código real que eles estão testando. Não é incomum passar mais tempo escrevendo o teste do que o código que está testando.

Isso é normal ou estou fazendo algo errado?

As perguntas “ são testes unitários ou desenvolvimento orientado a testes vale a pena? ”,“ Estamos gastando mais tempo implementando testes funcionais do que implementando o sistema em si, isso é normal? " e suas respostas são mais sobre se os testes valem a pena (como em "devemos pular testes escritos completamente?") . Embora eu esteja convencido de que os testes são importantes, estou imaginando se passar mais tempo em testes do que o código real é normal ou se sou apenas eu.

A julgar pelo número de visualizações, respostas e votos positivos recebidos, só posso assumir que é uma preocupação legítima que não é abordada em nenhuma outra pergunta no site.

    
por springloaded 14.10.2015 / 00:27
fonte

9 respostas

198

Eu me lembro de um curso de engenharia de software que gasta ~ 10% do tempo de desenvolvimento escrevendo um novo código, e os outros 90% são de depuração, teste e documentação.

Como os testes de unidade capturam a depuração e o esforço de teste em código (potencialmente capaz de automatizar), faria sentido que mais esforço fosse aplicado a eles; o tempo real gasto não deve ser muito maior do que o de depuração e teste que seria necessário sem escrever os testes.

Finalmente, os testes também devem funcionar como documentação! Deve-se escrever testes unitários na maneira como o código deve ser usado; ou seja, os testes (e uso) devem ser simples, coloque as coisas complicadas na implementação.

Se seus testes são difíceis de escrever, o código que eles testam provavelmente é difícil de usar!

    
por 14.10.2015 / 01:16
fonte
95

É.

Mesmo se você fizer apenas um teste de unidade, não é incomum ter mais código dentro dos testes do que o código realmente testado. Não há nada de errado com isso.

Considere um código simples:

public void SayHello(string personName)
{
    if (personName == null) throw new NullArgumentException("personName");

    Console.WriteLine("Hello, {0}!", personName);
}

Quais seriam os testes? Existem pelo menos quatro casos simples para testar aqui:

  1. O nome da pessoa é null . A exceção é realmente lançada? São pelo menos três linhas de código de teste para escrever.

  2. O nome da pessoa é "Jeff" . Nós obtemos "Hello, Jeff!" em resposta? São quatro linhas de código de teste.

  3. O nome da pessoa é uma string vazia. Qual saída esperamos? Qual é a saída real? Pergunta secundária: corresponde aos requisitos funcionais? Isso significa outras quatro linhas de código para o teste de unidade.

  4. O nome da pessoa é curto o suficiente para uma string, mas é muito longo para ser combinado com "Hello, " e o ponto de exclamação. O que acontece? ¹

Isso requer muito código de teste. Além disso, as partes mais elementares do código geralmente requerem um código de configuração que inicializa os objetos necessários para o código em teste, o que também muitas vezes leva a escrever stubs e simulações, etc.

Se a proporção for muito grande, você poderá verificar algumas coisas:

  • Existe duplicação de código nos testes? O fato de ser um código de teste não significa que o código deva ser duplicado (copiado e colado) entre testes semelhantes: essa duplicação dificultará a manutenção desses testes.

  • Existem testes redundantes? Como regra geral, se você remover um teste de unidade, a cobertura da filial deverá diminuir. Caso contrário, pode indicar que o teste não é necessário, uma vez que os caminhos já estão cobertos por outros testes.

  • Você está testando apenas o código que deve testar? Não se espera que você teste a estrutura subjacente das bibliotecas de terceiros, mas exclusivamente o código do projeto em si .

Com testes de fumaça, testes de sistema e integração, testes funcionais e de aceitação e testes de carga e estresse, você adiciona ainda mais código de teste, então ter quatro ou cinco LOC de testes para cada LOC de código real não é algo que você deve se preocupar sobre.

Uma nota sobre o TDD

Se você está preocupado com o tempo que leva para testar seu código, pode ser que você esteja fazendo errado, que é o primeiro código, testa mais tarde. Nesse caso, o TDD pode ajudar, incentivando você a trabalhar em iterações de 15 a 45 segundos, alternando entre o código e os testes. De acordo com os proponentes do TDD, ele acelera o processo de desenvolvimento reduzindo o número de testes que você precisa fazer e, mais importante, a quantidade de código de negócios a ser gravado e especialmente reescrita para testes.

¹ Deixe n ser o comprimento máximo de uma string . Podemos chamar SayHello e passar por referência uma string de tamanho n - 1 que deve funcionar bem. Agora, em Console.WriteLine step, a formatação deve terminar com uma cadeia de comprimento n + 8, o que resultará em uma exceção. Possivelmente, devido aos limites de memória, até mesmo uma string contendo n / 2 caracteres levará a uma exceção. A pergunta que se deve fazer é se este quarto teste é um teste unitário (parece um, mas pode ter um impacto muito maior em termos de recursos comparado com testes unitários médios) e se testa o código real ou o framework subjacente. / sup>

    
por 14.10.2015 / 00:49
fonte
58

Acho importante distinguir entre dois tipos de estratégias de teste: testes unitários e testes de integração / aceitação.

Embora o teste de unidade seja necessário em alguns casos, ele é frequentemente superutilizado. Isso é exacerbado por métricas sem sentido forçadas pelos desenvolvedores, como "100% de cobertura". O link fornece um argumento convincente para isso. Considere os seguintes problemas com testes de unidade agressivos:

  • Uma abundância de testes inúteis que não documentam um valor comercial, mas simplesmente existem para se aproximar dessa cobertura de 100%. Onde eu trabalho, nós temos que escrever testes unitários para fábricas que não fazem nada além de criar novas instâncias de classes. Não acrescenta valor. Ou aqueles longos métodos .equals () gerados pelo Eclipse - não há necessidade de testá-los.
  • Para facilitar o teste, os desenvolvedores subdividiriam algoritmos complexos em unidades testáveis menores. Soa como uma vitória, certo? Não, se você precisa ter 12 classes abertas para seguir um caminho de código comum. Nesses casos, o teste de unidade pode diminuir a legibilidade do código. Outro problema com isso é que se você dividir seu código em pedaços muito pequenos, você acaba com uma magnitude de classes (ou pedaços de código) que parece não ter justificativa além de ser um subconjunto de outro pedaço de código.
  • A refatoração de código altamente protegido pode ser difícil, já que você também precisa manter a abundância de testes de unidade que dependem dele funcionando apenas para . Isso é exacerbado por testes de unidade comportamental, em que parte do teste também verifica a interação dos colaboradores de uma turma (geralmente ridicularizados).

O teste de integração / aceitação, por outro lado, é uma parte extremamente importante da qualidade do software e, na minha experiência, você deve gastar uma quantidade significativa de tempo para acertá-los.

Muitas lojas beberam o Kool-Aid TDD, mas como mostra o link acima, vários estudos mostram que seu benefício é inconclusivo.

    
por 14.10.2015 / 09:58
fonte
10

Não pode ser generalizado.

Se eu precisar implementar uma fórmula ou algoritmo de renderização fisicamente baseada, pode muito bem ser que eu gaste 10 horas em testes unitários paranoicos, já que sei que o menor erro ou precisão pode levar a erros que são quase impossíveis de diagnosticar meses depois.

Se eu quiser agrupar logicamente algumas linhas de código e atribuir um nome, usado apenas no escopo do arquivo, talvez eu não o teste (se você insistir em escrever testes para cada função, sem exceção, programadores pode voltar a escrever o mínimo possível de funções).

    
por 14.10.2015 / 10:38
fonte
3

Sim, é normal se você estiver falando sobre o TDD. Quando você tem testes automatizados, você protege o comportamento desejado do seu código. Quando você escreve seus testes primeiro, você determina se o código existente já tem o comportamento desejado.

Isso significa que:

  • Se você escrever um teste que falhe, corrigir o código com a coisa mais simples que funciona é mais curto do que escrever o teste.
  • Se você escrever um teste aprovado, não haverá código extra para escrever, gastando efetivamente mais tempo para escrever um teste do que um código.

(Isso não leva em conta a refatoração de código, que visa gastar menos tempo escrevendo código subseqüente. Ela é balanceada pela refatoração de teste, que visa gastar menos tempo escrevendo testes subsequentes.)

Sim, também, se você estiver falando sobre testes de escrita após o fato, então estará gastando mais tempo:

  • Determinando o comportamento desejado.
  • Determinar formas de testar o comportamento desejado.
  • Cumprindo as dependências de código para poder gravar os testes.
  • Corrigindo o código para testes que falham.

Do que você vai gastar realmente escrevendo código.

Então, sim, é uma medida esperada.

    
por 14.10.2015 / 10:37
fonte
3

Eu acho que é a parte mais importante.

O teste de unidade nem sempre é sobre "ver se funciona direito", é sobre aprender. Depois de testar algo suficiente, ele se torna "codificado" em seu cérebro e você acaba diminuindo o tempo de teste da unidade e pode se ver escrevendo classes e métodos inteiros sem testar nada até que seja feito.

É por isso que uma das outras respostas nesta página menciona que em um "curso" eles fizeram 90% de testes, porque todos precisavam aprender as ressalvas de seu objetivo.

O teste de unidade não é apenas um uso muito valioso do seu tempo, pois ele literalmente melhora suas habilidades, é uma boa maneira de rever seu próprio código novamente e encontrar um erro lógico ao longo do caminho.

    
por 14.10.2015 / 06:16
fonte
2

Pode ser para muitas pessoas, mas depende.

Se você está escrevendo testes primeiro (TDD), pode estar ocorrendo alguma sobreposição no tempo gasto escrevendo o teste, o que realmente ajuda a escrever o código. Considere:

  • Determinando resultados e entradas (parâmetros)
  • Convenções de nomenclatura
  • Estrutura - onde colocar as coisas.
  • Pensamento antigo simples

Ao escrever testes depois de escrever o código, você pode descobrir que o seu código não é facilmente testável, portanto, escrever testes é mais difícil / leva mais tempo.

A maioria dos programadores tem escrito muito mais tempo do que os testes, então eu esperaria que a maioria deles não fosse tão fluente. Além disso, você pode adicionar o tempo necessário para entender e utilizar sua estrutura de testes.

Acho que precisamos mudar nossa mentalidade sobre quanto tempo leva para codificar e como o teste de unidade está envolvido. Nunca olhe para ele a curto prazo e nunca apenas compare o tempo total para entregar um recurso em particular, porque você tem que considerar não apenas escrever código melhor / menos defeituoso, mas código que é mais fácil de alterar e ainda assim torná-lo melhor / menos buggy.

Em algum momento, todos somos capazes apenas de escrever códigos tão bons, então algumas das ferramentas e técnicas podem oferecer tanto para melhorar nossas habilidades. Não é como se eu pudesse construir uma casa se tivesse apenas uma serra guiada a laser.

    
por 14.10.2015 / 01:13
fonte
2

Is it normal to spend as much, if not more, time writing tests than actual code?

Sim, é. Com algumas ressalvas.

Primeiro de tudo, é "normal" no sentido de que a maioria das grandes lojas funciona dessa maneira, então, mesmo que esse caminho fosse completamente equivocado e estúpido, o fato de que a maioria das grandes lojas funciona dessa maneira faz com que seja "normal" .

Por isso, não quero dizer que testar isso está errado. Eu trabalhei em ambientes sem testes, e em ambientes com testes obsessivo-compulsivos, e ainda posso lhe dizer que até mesmo o teste obsessivo-compulsivo foi melhor do que nenhum teste.

E eu não faço TDD ainda, (quem sabe, eu posso no futuro), mas eu faço a grande maioria dos meus ciclos de edição-depuração executando os testes, não o aplicativo real, então naturalmente, eu trabalho muito nos meus testes, para evitar, tanto quanto possível, executar a aplicação real.

No entanto, esteja ciente de que há perigos em testes excessivos e, especificamente, na quantidade de tempo gasto com manutenção dos testes. (Estou escrevendo principalmente essa resposta para apontar especificamente isso.)

No prefácio de A arte de testar a unidade de Roy Osherove (Manning, 2009) o autor admite ter participado de um projeto que falhou em grande parte devido à tremenda carga de desenvolvimento imposta por testes unitários mal concebidos que tiveram que ser mantidos durante todo o esforço de desenvolvimento. Portanto, se você está gastando muito tempo fazendo nada além de manter seus testes, isso não significa necessariamente que você está no caminho certo. caminho certo, porque é "normal". Seu esforço de desenvolvimento pode ter entrado em um modo insalubre, em que um repensar radical de sua metodologia de teste pode ser necessário para salvar o projeto.

    
por 06.12.2015 / 17:47
fonte
0

Is it normal to spend as much, if not more, time writing tests than actual code?

  • Sim para escrever testes unitários (teste um módulo isoladamente) se o código for altamente acoplado (legado, não é suficiente separação de interesses , faltando Injeção de dependência , e não tdd desenvolvido
  • Sim para escrever integração / aceitação-testes se a lógica a ser testada for alcançável via gui-code
  • Não para escrever testes de integração / aceitação, desde que o código gui e a lógica de negócios estejam separados (o teste não precisa interagir com o gui)
  • Não para escrever testes unitários se houver uma série de preocupações, injeção de dependência, código testdiven developped (tdd)
por 14.10.2015 / 10:36
fonte