Como qualquer aspecto do assunto desenvolvimento de software esse também gera dúvidas, polêmicas, discursos pró e contra.
Não é minha intenção defender ou atacar a técnica, o objetivo é tentar esclarecer como ela nasceu e seus objetivos.
O primeiro e mais polêmico "mito" é que TDD é menos produtivo.
Muitos profissionais gostam de escrever pequenos trechos de código para garantir que a idéia funciona e depois partir
para o projeto com a idéia mais madura. Esses pequenos códigos geralmente são provas de conceito (poc) e ajudam o processo
de modelagem do sistema, já que influênciam as decisões.
Ainda nessa linha, outros preferem escrever códigos e depois usar as
ferramentas de depuração das IDE para verificar se tudo funciona.
Acho que vale algumas palavras sobre o assunto: "Teste Unitário como artefato para execução automática", já que ele é um instrumento importante
na visão de muitos profissionais (até mesmo para alguns desenvolvedores, ainda não acostumados com essa prática ou sem vontade de construir os artefatos).
O Teste Unitário
Esse tipo de teste visa testar a "menor parte testável" de um software (por exemplo: os métodos de uma classe), com foco na
entrada e saída, testando valores válidos e inválidos. Como a abordagem descrita propõe um "teste caixa preta" (onde a implementação
não precisa ser conhecida) qualquer profissional poderia escrever esses testes, desde que conhecesse as regras de negócio.
Os testes devem verificar:
- Se para cada valor *válido* informado na entrada, uma saída apropriada foi gerada
- Se para cada valor *inválido* informado na entrada, somente uma saída apropriada foi gerada
- Se para cada estado *válido* na entrada, ocorrência de estado válido na saída
- Se para cada estado *inválido* na entrada, ocorrência de estado inválido na saída
Durante o desenvolvimento de software sempre testamos partes dele, seja usando papel e lápis (teste seco ou de mesa), escrevendo pequenos
trechos de código, mentalmente, etc... então testes unitários são naturalmente parte do trabalho.
Porque então não são populares?
Na verdade, são bem populares.
O que não é muito popular é a finalidade (objetivo) desse tipo teste.
Não é o objetivo do teste unitário garantir que o software seja "bug free" (sem erros). Erros acontecem e quanto antes percebemos melhor.
É objetivo do teste garantir que a "menor parte testável" esteja funcionando.
Por exemplo, se um método de uma classe torna o estado de um objeto (instância dessa classe) inválido, além de outros aspectos, algum teste automático deveria avisar sobre essa condição e falhar.
Analogamente, se um método não cumpre seu objetivo (não faz o que deveria), um outro teste automático deveria avisar a condição.
Muitas vezes esse processo de testes é realizado com técnicas que não permitem a execução automática, assim a cada mudança no código alguém teria que executar novamente os passos para garantir que tudo funciona.
Hoje em dia é praticamente inconcebível que você não execute determinado trecho do código, afinal com as IDEs disponíveis basta apertar um botão para executar passo a passo, mas nem sempre foi possível fazer isso.
A evolução dos ambientes de desenvolvimento acabaram deixando as pessoas mais preguiçosas e é natural, por que eu ficaria imaginando o resultado de alguma implementação, se posso simplesmente executar e ver o resultado?
Como o software é o "conjunto de engrenagens" que permite usarmos o computador como uma máquina, sua característica abstrata pode ser confusa, executá-lo é como "girar uma alavanca" para ver os mecanismos funcionando, ou seja é um modo "concreto" de observá-lo.
Apesar de soar estranho "girar uma alavanca" é exatamente o tipo de comparação que eu gostaria de apresentar, criar um artefato como um teste unitário automático é como participar da Revolução Industrial, estaremos usando um "robô" (uma máquina) para realizar a tarefa, não criá-los porém é como voltar ao século XVII.
Os robôs são ótimos para executar tarefas repetitivas, não se esquecem de um passo no processo, não "faltam no trabalho" e estão a disposição de qualquer pessoa e/ou robô.
Apesar da natureza do teste permitir que qualquer profissional que conheça as regras de negócio possa criá-lo, escrever código para testar o código só faz sentido para o desenvolvedor. Além da prática com a linguagem, é (teoricamente) o maior interessado em saber se o código que escreveu atende seu objetivo, ou seja, se funciona (faz o que deveria).
Antes de criar seu próximo "printf" (cout, println, etc...) pense em escrever um teste que valide alguma condição, o código não dependerá mais do seu olho atento ao console para identificar uma cadeia de caracteres peculiar, melhor ainda, não dependerá dos olhos de qualquer outra pessoa para ser identificado como "certo ou errado".
Refactory (ou code refactoring)
É praticamente qualquer alteração no código para melhorar algum aspecto (seja diminuir a complexidade, melhorar a arquitetura, melhorar a modelagem, melhorar a testabilidade, ou simplesmente torná-lo mais legível), sem alterar o "comportamento externo", ou seja, o domínio e contra-domínio são mantidos (dado uma entrada x, o resultado é y, antes e depois da modificação).
Então, como podem testes automáticos melhorarem o design da minha aplicação?
Se você e sua equipe começarem a pensar nos objetivos da aplicação e criar os artefatos de teste unitários (para execução automática) para determinar os cenários, quando terminarem o conjunto de testes, o código que será criado para passar nesses testes tende a ser mais coerente (primeira versão da modelagem).
Assim que todas as condições dos testes forem atendidas (o código passa em todos os testes) começam os refactories. É muito mais fácil e "seguro" fazer refactory em uma aplicação com testes para garantir os objetivos, dessa maneira, não é preciso se preocupar em quebrar alguma funcionalidade, já que, desde que o teste esteja passando, a funcionalidade está garantida.
Muito bem, mas quem vai testar se os testes estão realmente de acordo com o que foi especificado?
Ou seja, quem vai garantir os testes? Os testes dos testes?
A equipe vai manter e garantir que os testes estejam de acordo.
Por exemplo: se o resultado do teste foi positivo, mas algum comportamento parece incorreto, tem algo errado com o teste e/ou código. Uma revisão geral deixará claro o motivo e aplicando a correção dos problemas, temos de volta o cenário ideal.
Vale uma dica: sempre que existir um problema como esse, ou outros complicados, resolver os problemas é a prioridade máxima, não existe nada mais importante, a não ser que você goste de cenários caóticos na véspera de entrada em produção e/ou em produção.
TDD
Repita até o sucesso:
- Escreva os testes (novas funcionalidades ou melhorias)
- Escreva um código que passa nos testes (o mínimo possível, não se preocupe em acertar na primeira vez, nem em fazer algo muito elaborado)
- Refactory (Melhore o código até atingir os padrões desejados)
É isso!