Um fator importante que torna os testes de unidade extremamente úteis é o feedback rápido .
Considere o que acontece quando você tem seu aplicativo totalmente coberto com testes de integração / sistema / funcional (que já é uma situação ideal, longe da realidade na maioria das lojas de desenvolvimento). Geralmente, eles são executados por uma equipe de testes dedicada.
- Você confirma uma alteração no repositório SCM,
- algum tempo (possivelmente dias) depois, os testadores recebem um novo release interno e começam a testá-lo,
- eles encontram um erro e enviam um relatório de erro,
- (no caso ideal) alguém atribui o relatório de erro a você.
Isso tudo pode levar dias ou até semanas. A essa altura, você já estava trabalhando em outras tarefas, para não ter os mínimos detalhes do código escritos anteriormente. Além disso, você normalmente não tem nenhuma prova direta de onde realmente está o erro, portanto, leva um tempo considerável para encontrar e corrigir o erro.
Considerando que no teste de unidade (TDD)
- você escreve um teste,
- você escreve algum código para satisfazer o teste,
- o teste ainda falha,
- você olha o código e normalmente tem uma experiência de "oops" em alguns segundos (como "oops, esqueci de verificar essa condição!") e, em seguida,
- corrija o erro imediatamente.
Tudo isso acontece em questão de minutos .
Isso não quer dizer que testes de integração / sistema não sejam úteis; eles apenas servem a propósitos diferentes. Com testes de unidade bem escritos, você pode capturar uma grande proporção dos erros no código antes que eles cheguem à fase de integração, onde já é consideravelmente mais caro encontrá-los e corrigi-los. Você está certo de que os testes de integração são necessários para detectar os tipos de bugs difíceis ou impossíveis de serem detectados nos testes de unidade. No entanto, na minha experiência, esses são os mais raros; a maioria dos erros que eu vi são causados por alguma omissão simples ou até trivial em algum lugar dentro de um método.
Sem mencionar que o teste de unidade também testa suas interfaces quanto à usabilidade / segurança, etc., fornecendo um feedback de vital importância para melhorar seu design e APIs. Qual IMHO pode reduzir consideravelmente as chances de erros de integração de módulos / sistemas: quanto mais fácil e limpa uma API for, menor a chance de mal-entendidos ou omissões.
O que você tem com testes de unidade automatizados, testes de integração automatizados e testes de aceitação automatizados? Na sua experiência, o que gerou o maior ROI? e porque?
O ROI depende de muitos fatores, provavelmente o principal deles é se o seu projeto é greenfield ou legado. Com o desenvolvimento greenfield, meu conselho (e experiência até agora) é fazer testes de unidade no estilo TDD desde o início. Estou confiante de que este é o método mais econômico neste caso.
Em um projeto legado, no entanto, construir uma cobertura suficiente de testes de unidade é uma tarefa enorme , que será muito lenta para gerar benefícios. É mais eficiente tentar cobrir a funcionalidade mais importante com testes de sistema / funcionais via interface do usuário, se possível. (pode ser difícil testar aplicativos GUI de desktop automaticamente através da GUI, embora as ferramentas de suporte a testes automatizados estejam melhorando gradualmente ...). Isso fornece uma rede de segurança grossa, porém eficaz, rapidamente. Em seguida, você pode começar a criar gradualmente testes de unidade nas partes mais críticas do aplicativo.
Se você tivesse que escolher apenas uma forma de teste para automatizar em seu próximo projeto, qual seria?
Essa é uma questão teórica e acho inútil. Todos os tipos de testes têm seu uso na caixa de ferramentas de um bom engenheiro de SW, e todos eles têm cenários em que são insubstituíveis.