Porque você definitivamente precisa de testes de unidade

(AVISO: Este post é uma tradução livre do excelente post originalmente publicado por Colin Dembovski, também MVP de Visual Studio ALM, em http://www.colinsalmcorner.com/2013/07/why-you-absolutely-need-to-unit-test.html. Thanks, Colin!)


Porque você definitivamente precisa de testes de unidade

Eu escrevi sobre porque builds são absolutamente essenciais no desenvolvimento moderno de aplicações (parte 1) e por que o Team Build é um grande mecanismo de build (parte 2). No entanto, se você não inclui testes de unidade em suas builds, é como escovasse os dentes sem creme dental – há muito movimento, mas não é a maneira mais eficaz de se fazer as coisas. Neste post, quero propor algumas reflexões sobre por que você definitivamente precisa usar testes de unidade.

Aqui vão alguns destaques:

  1. Codificar com testes de unidade em mente obriga-o a pensar sobre o design e a arquitetura de seu código – o que faz com que ele se torne um código melhor
  2. Testes de unidade fornecem feedback imediato – todas as alterações no código são testadas (pelo menos em parte) à medida que você codifica.
  3. Um pequeno investimento agora leva a uma enorme economia mais tarde – bugs em produção custam muito mais do que bugs em desenvolvimento
  4. Testes de unidade fornecem métricas para a qualidade do seu código
  5. Testes de unidade trazem qualidade inerente às suas implantações (releases)

Vamos analisar cada um desses pontos.

Desenvolvimento guiado pelo pensar

Você provavelmente já ouviu falar de termos como “test-driven development (TDD)” (desenvolvimento guiado por testes) ou “behavior-driven development (BDD)” (desenvolvimento guiado por comportamento). Adoraria cunhar mais um termo – desenvolvimento guiado pelo pensar. Eu venho de um histórico de desenvolvimento – e como eu amo codificação (como a maioria dos desenvolvedores), sofro da síndrome de “deixe-os-detalhes-para-lá-quero-apenas-começar-a-codar”. A maioria dos desenvolvedores que conheço também. Espero que você nunca perca esta paixão – mas há muito a ser dito para tomarmos fôlego e pensarmos primeiro.

Se você apenas pular no código e não tiver testes, você provavelmente vai criar algo que funcionará de imediato para algumas situações (geralmente com a ressalva do “funciona na minha máquina!?!”). No entanto, isso não é sustentável já que leva ao que um  professor universitário meu costumava chamar de “código espaguete” (o que foi especialmente engraçado já que ele é italiano). Você escreve algum código e o implanta. Algo quebra. Você faz um remendo e reimplanta. Agora você quebrou alguma outra coisa. Então você põe um pouco de fita adesiva (metaforicamente, é claro). Eventualmente você chega ao ponto do chiclete e da folha de alumínio, e as coisas vão de mal a pior.

Se você começa a escrever testes (ou pelo menos começa a escrever seu código com testes em mente), é mais provável que você escreva um código melhor. Se você alguma vez tentou escrever testes de unidade para código legado, vai entender o que quero dizer. Se você não codificar com testes em mente, seu código (como seria de se esperar) acaba não-testável (ou pelo menos muito difícil de testar com testes de unidade). Escrever código testável o obriga a pensar em boa decomposição, boas interfaces, boa separação de preocupações (SoC, separation of concerns), inversão de controle (IoC, inversion of control) e injeção de dependências (DI, dependency injection) e um montão de outros princípios que todos nós aprendemos, mas de alguma forma esquecemos de usar em nossa rotina diária.

Então comece com seus testes. Isto o obriga a usar todas as coisas boas (não são chamadas de boas práticas por acaso). O pouco de pensamento necessário no início vai te poupar muita dor ao longo do tempo (para não falar da diminuição da quantidade de chiclete que você vai encontrar no seu repositório de fontes).

Feedback imediato

Deixe-me fazer uma pergunta – quão longo é o ciclo de feedback entre o momento em que você escreve algum código e quando você obtém feedback sobre a validade desse código? Ou formulado de outra maneira, pense sobre o último bug que você consertou. Qual é a duração do tempo entre a escrita do código e o relatório do bug? Um dia? Uma semana? Um ano?

O fato é que quanto mais cedo depois de escrever o código que você encontrar um bug, mais barato será para conserta-lo. Vamos considerar duas extremidades do espectro: tempo de codificação e em produção.

Quando existe um bug em produção, pode levar algum tempo para encontra-lo. Em seguida, é relatado para o suporte técnico. Eles investigam. Eles então escalam para o suporte de segundo nível. Eles investigam. Eles então escalam para os desenvolvedores, que tem que investigar e reproduzir. Em outras palavras, um longo tempo.

E quando você está codificando? Você escreve algum código e executa os testes de unidade. O teste falha. Você investiga e corrige. Em outras palavras, muito pouco tempo.

Quanto mais rápido você receber feedback (e quanto mais vezes você obtiver feedback), mais eficiente e eficaz você será. Numa larga escala, essa é em geral a razão para metodologias Ágeis – a iteração rápida aumenta a frequência do ciclo de feedback. Isso funciona no nível de gerenciamento de projeto, e também funciona no nível de codificação.

Eis com o que isso se parece em um gráfico:

clip_image001
350

Quanto mais cedo você encontrar seus bugs, tão menos tempo que você vai gastar encontrando-os e mais barato será para corrigi-los. Se você não tem testes de unidade, você já vai mudar sua primeira oportunidade de feedback para a terceira zona (teste manual). Se você não fizer o teste manual, você está empurrando de novo ainda mais para fora em direção aos testes de aceitação do usuário (UAT, user acceptance test). Se você não fizer isso, então o primeiro feedback que você vai conseguir é da produção – que é o mais demorado e o mais caro.

Obter feedback imediato enquanto você está codificando é ótimo quando você está fazendo projetos “greenfield” (projetos que nunca foram implantados antes). Ele realmente começa a brilhar quando você volta depois de 6 meses (ou 12 ou 18) para adicionar recursos – você ainda tem sua suíte de testes para garantir que você não está quebrando nada com o seu novo código.

Gastar um pouco agora – economizar muito lá na frente

Quase nunca falha: os times que não têm testes de unidade alegam que “não têm tempo para teste de unidade”. Defendo que na grande maioria dos casos, é exatamente porque não se investe em testes de unidade que você não tem tempo para codificar corretamente. Sempre que você implanta código não-testado, você aumenta sua dívida técnica. O fato triste é que a dívida técnica tende a crescer exponencialmente.

Olhe novamente para o gráfico acima. Onde você acha que você vai gastar mais tempo para encontrar e corrigir bugs? Obviamente, quanto mais “à direita” você está, mais tempo você precisa para corrigir um bug. Então invista um pouco “agora” enquanto você está codificando, de modo que você não tem que gastar muito tempo mais tarde lidando com problemas em produção!

Métricas de qualidade

Como se mede a qualidade do seu código? Erros na produção por período de tempo? Tempo médio de reparo (MTTR, Mean Time To Resolve)? A maioria dessas medições é útil em algum grau, mas como são estatísticas de “tempo de produção”, o feedback vem muito tarde no ciclo.

Os time com que trabalho que tem métricas invariavelmente têm métricas de teste de unidade – taxas pass/fail e estatísticas de cobertura de código. Os times que não têm métricas quase sempre não têm testes unitários. Testes unitários fornecem uma medida objetiva da qualidade do seu código. Como você pode melhorar se você não sabe onde você está atualmente?

Qual cobertura que você deve estar almejando? Isto é discutível, mas eu sempre recomendo que você prefira se concentrar em suas tendências – certifique-se que cada implantação é melhor que a última. Dessa forma, o número absoluto realmente não importa. Mas a única maneira que você pode fazer qualquer tipo de análise de tendências é se você realmente está coletando as métricas. É por isso que adoro testes de unidade (com cobertura de código) no TFS Team Build, uma vez que as métricas são agrupadas dentro do data warehouse e é realmente fácil de fazer relatórios de tendências. Mostrar à área de negócios um gráfico com uma constante melhoria da qualidade é uma das melhores coisas que você pode fazer como um desenvolvedor!

Qualidade inerente

Todas estas práticas levam à qualidade inerente – uma qualidade que faz parte intrínseca do que você está fazendo do dia-a-dia, ao invés de algo que você coloca no final de uma iteração (ou rapidamente antes do lançamento). Se você incluir qualidade inerente em seus processos e práticas, você pode dormir tranquilamente na noite da grande implantação. Se não o fizer, você vai ter que manter o telefone por perto para todas aquelas chamadas inevitáveis de “Socorro, o sistema está quebrado…”

Código legado

Na minha empresa anterior, tivemos grandes quantidades de código “legado” – que estava em produção, que nós mantínhamos constantemente, e que não tinham testes. Quando comecei a direcionar para testes de unidade e cobertura, inevitavelmente chegamos ao momento embaraçoso onde alguém deixou escapar, “Mas tem muito código para começar a testar agora!”. Argumentei que 1 teste era melhor do que 0 testes. E 2 testes são melhores que 1 teste e assim por diante.

Então criamos uma política em que qualquer código em que você trabalhou (sejam novos recursos ou correções) precisava ter testes de unidade. Isto nos colocou no caminho para a construção de nossa suíte de testes abrangentes. Também definimos uma política de que a cobertura de código tinha que ser maior nessa implantação do que na implantação anterior para que a entrada em produção fosse aprovada. No começo, nossa cobertura foi 1,5 ou 2%. Depois de apenas 6 meses, estávamos perto dos 40% de cobertura. E o número de problemas de produção em que estávamos trabalhando diminuiu dramaticamente.

Conclusão

Assim como você não pode deixar de fazer builds automatizadas, você realmente não pode se dar ao luxo de não fazer testes de unidade. O investimento agora proporcionará benefícios enormes ao longo do ciclo de vida do seu aplicativo – não só para você e seu time, mas também para o seu negócio e as partes interessadas também.

Para ler mais, Martin Hinshelwood escreveu um ótimo post sobre o test-first que eu recomendo.

Feliz Testes!