Por que TDD?
Autor: Vinicius Assef | Publicado: 2016-10-24T18:19:00-02:00



**Observações:**

1. Esse artigo é focado em iniciantes, por isso eu resolvi usar um estilo mais popular, evitando termos muito técnicos relacionados a testes de software.

1. Não pretendo esgotar o assunto sobre TDD nem sobre teste de software, que são campos muito amplos de estudo. O que busco aqui é simplesmente fazer uma introdução didática ao assunto.

Agora, vamos ao que interessa.


Sempre ouvimos que praticar TDD é importante e que é vantajoso. Mas qual a vantagem? Por que, exatamente, TDD é importante e em que ele pode me ajudar?

Antes de falarmos sobre TDD, lembre-se que existem dois conceitos maiores: Testes automatizados e _Test First_.

Vamos começar por testes automatizados.

Se você já conhece sobre testes automatizados e _Test First_ pode pular para "Explicando TDD", lá embaixo. Se você tem dúvidas sobre o assunto, sugiro ler todo o artigo, principalmente a seção sobre _Test First_.


Testes automatizados
--------------------

Imagine entrar tela por tela, opção por opção, funcionalidade por funcionalidade do sistema e fazer ali dentro tudo que for possível para certificar que ele está funcionando corretamente. Imagine fazer isso para todos os perfis de usuário existentes: gerentes, vendedores, revisores, administradores, clientes, usuários visitantes, etc. Imagine, além disso, testar a integração do seu sistema com outros sistemas. Por exemplo, enviar/receber arquivos de movimento bancário, ler últimos tweets, avisar usuários sobre reposição de estoque, enviar emails, etc.

Testar uma aplicação exige muito esforço e não é simples, além de ser uma tarefa repetitiva. Eu já trabalhei em lugares onde pessoas eram pagas para fazer todos os testes manualmente, todos os dias. Adivinha o que acontecia? Acertou se você disse que nem tudo era testado. Certamente esse problema não acontecia somente onde eu trabalhava porque devido a esse cenário surgiu a ideia de fazer com que o computador desempenhasse a função de um robô e executasse todos os testes, avisando quais deram errado.

Esse é o conceito de testes automatizados: usar um "robô" -- seu computador -- para testar o sistema inteiro com uma certa frequência.

Existem diversas abordagens para testes automatizados e há um certo problema de nomenclatura nessa área, mas é certo que testar um sistema inteiro é tarefa demorada, mesmo para computadores, e exige um ambiente controlado. Afinal, você não pode enviar um email para um cliente se ele, de fato, não fez um pedido de compra, certo?

Apesar de o computador ajudar bastante nos testes, ele não é capaz de fazer tudo. Um ser humano precisa dizer o que é o certo ou o errado em cada situação, em cada cenário de teste.

Daí surge o outro conceito, que citei no início do artigo, _Test First_.


_Test First_
------------

_Test First_ significa "primeiro, o teste".

Como assim? É que você deve escrever o teste antes. Mas antes de que? Antes de existir o que você quer testar.

É muito estranho a princípio, mas faz sentido e a forma de pensar é natural. Fazemos isso o tempo todo quando desenvolvemos software, mas não percebemos. Acompanhe meu racioncínio.

Quando um cliente te diz o que ele precisa, ele está te contando o que ele espera como resultado da funcionalidade. Exemplo:

> Eu quero ver na tela de consultar estoque, além da quantidade em estoque do produto, se essa quantidade está dentro dos limites mínimo e máximo para aquele produto.

Com essa sentença acima, o cliente disse várias coisas:

1. Precisa haver uma funcionalidade para "consultar estoque do produto".

1. Nessa funcionalidade ele quer ver a quantidade em estoque do produto informado.

1. O usuário precisa informar o produto para o estoque ser mostrado, obviamente.

1. Ele também quer ver, de alguma forma, se a quantidade em estoque está dentro dos limites permitidos para o produto.

Perceba que o cliente não disse como você tem que buscar esses dados. Nem como você deve calcular o estoque. Ele disse o que ele quer ver como resultado.

É claro que para terminar essa funcionalidade algumas coisas precisam ser esclarecidas. Por exemplo, como mostrar se a quantidade está dentro dos limites? Seria com uma mensagem? Com uma cor diferente? Isso ainda precisa ser detalhado. Mas já temos o básico, a espinha dorsal.

Nesse ponto já começamos a imaginar como seria a tela de resultado, quais campos mostrar, quais tabelas ler para juntar os dados, etc.

Em nossa cabeça, fazemos uma mistura de "o que eu quero como resultado" com "qual caminho devo seguir para atingir o resultado desejado". Todo mundo faz isso.

A técnica _Test First_ nada mais é do que transformar em código (programa mesmo) a primeira etapa -- "o que eu quero como resultado" -- antes de programar a segunda. _Test First_ precisa estar associado a outras técnicas para surtir efeito positivo. Por exemplo, desenvolvimento iterativo com feedback constante -- um dos pilares das metodologias ágeis.

Então, a cada funcionalidade que vou desenvolver, eu vejo o que o cliente quer e escrevo um programa para checar se o resultado do que eu vou desenvolver atende ao requisito. De fato, o programa de teste é o primeiro cliente do código de produção.

Claro que se eu rodar meu programa de teste antes de desenvolver a funcionalidade, ele vai me mostrar erros porque a funcionalidade ainda não existe. E esse é o espírito da coisa: ver um teste dando errado e, a cada etapa que eu avanço no desenvolvimento da funcionalidade real, os erros vão mudando, até que ela funcione como esperado.

Não é mágica. É esforço de desenvolvimento mesmo!

Normalmente surge uma pergunta muito comum:

> Mas eu posso escrever o teste depois que a funcionalidade estiver pronta. Eu não mudaria minha forma de pensar, com a qual já estou acostumado há anos.

Sim, certamente você pode fazer assim. Muita gente faz assim. A questão é que _Test First_ te força a formalizar o resultado esperado primeiro, para depois atendê-lo. Você passa a ter um objetivo real a atingir: fazer o teste passar. E escrever testes é mais ou menos como escrever documentação: se deixar para depois, você nunca vai fazer.

Esse é o conceito de _Test First_, mas onde TDD entra nessa história?


Explicando TDD
--------------

Sendo uma prática, TDD não é usado; TDD é praticado.

TDD une os dois conceitos acima e os amplia. É uma demonstração que o todo é maior do que a soma das partes. Se você praticar TDD, estará praticando testes automatizados, _Test First_ e algo mais.

TDD é a sigla de _Test Driven Development_ (Desenvolvimento Guiado pelos Testes). Significa que os testes vão guiar o desenvolvimento. _"Mas guiar em qual sentido?"_, você pode perguntar. Guiar no sentido de indicar se a sua arquitetura está indo na direção certa.

Um dos objetivos de TDD é te mostrar se o que você está fazendo está simples de ser testado. Se for complicado preparar o caminho para fazer o teste, isso pode ser um indício de problema.

Lembra quando eu falei que para rodar os testes automatizados o sistema precisaria estar em um ambiente controlado? É esse ambiente controlado que TDD te ajuda a criar. Um ambiente onde o envio de e-mail não envia uma mensagem de fato, mas simula o envio. Um ambiente onde o arquivo de transferência bancária não é transferido de fato, mas é simulado. Se estiver difícil preparar seu programa para fazer essas simulações, você precisa mexer na arquitetura do sistema para deixar essa simulação (teste) mais simples. Outra vantagem do ambiente simulado é que você pode e deve aplicar [técnicas para acelerar os testes](/2016/09/como-escrever-testes-rapidos.html).

Em outras palavras, um dos objetivos de TDD é tornar seu sistema simples para ser testado. Se ele for complicado para testar, será complicado para adaptar futuramente.

O outro objetivo é saber se ele funciona como deveria, em um ambiente de testes.

Se os testes guiam o desenvolvimento, um outro benefício de praticar TDD é escrever somente o que for necessário para atender o que o cliente precisa. Nada mais, nada menos. Se o teste passou, paramos por aí.

Quando o cliente pedir uma modificação qualquer, seguimos os seguintes passos:

1. Modificamos primeiro o programa de teste para adequá-lo ao novo requisito.

1. Rodamos o teste para certificar que ele vai mostrar um erro. Afinal, ainda não ajustamos o sistema real.

1. Alteramos o sistema para atender ao teste.

1. Rodamos o teste para ver o resultado OK.

Depois de ver o teste funcionando, podemos melhorar o código do sistema, para que ele fique mais rápido ou mais organizado, por exemplo. A cada modificação, rodamos os testes. Se mostrarem algum erro, corrigimos o sistema. Se mostrarem resultado OK, significa que melhoramos o sistema sem estragá-lo.

A esse ciclo chamamos de _Red, Green, Refactor_ (Vermelho, Verde, Refatorar). [Pequenos passos](/2016/04/pq-desenvolver-um-sistema-usando-baby-steps.html) para atingir grandes objetivos.

Quantas vezes mexemos em um ponto do sistema, testamos manualmente, para só depois de implantar, percebermos que afetamos uma outra parte, que aparentemente não tinha nada a ver com a alteração? Quem nunca passou por isso?

Eis aqui outra vantagem do TDD: você tem a segurança de mexer no sistema sem estragá-lo.

Eu costumo dizer que os testes funcionam como uma rede de proteção contra defeitos. Essa rede deve ser mais forte ou menos forte dependendo do peso que ela deve aguentar. Se o seu sistema for simples, ela não precisa ser tão forte. Se o sistema for muito complexo, ela deve ser bastante reforçada.

Mas nem tudo são flores no desenvolvimento de software, nem praticando TDD. Também temos espinhos. Como diz um amigo meu:

> Testar um sistema não significa que ele esteja certo. Significa apenas que ele passou no teste.

Essa é uma verdade. Por isso é provável que você se depare com erros em produção, mesmo praticando TDD da maneira correta.

Lembra quando eu disse que um ser humano precisa dizer ao computador o que considerar como certo ou como errado? Nós, seres humanos, falhamos e podemos deixar de testar algumas situações importantes. Quando isso acontece, o sistema dá erro. TDD não livra você disso.

Nesse caso a primeira coisa a fazer é escrever um teste para simular a situação de erro e ver o teste apontando esse problema. Aí você está livre para corrigir o sistema e ver o teste mostrando resultado OK. Agora você identificou outro benefício do TDD: evitar que os mesmos erros -- _regressions_ -- ocorram novamente.

Outro benefício comumente detectado por quem pratica TDD é que você praticamente abandona os debuggers. Como você está sendo guiado pelo teste e executa-o com frequência até que o resultado esteja correto, tem controle sobre todas as camadas de software do sistema. Assim, a depuração torna-se desnecessária. Eu mesmo não lembro quando precisei usar o debugger em Python. Vale destacar que eu desenvolvo em Python para web. Talvez outros ambientes continuem necessitando do debugger.


Conclusão
---------

Testes automatizados são muito úteis, mas os testes manuais não devem ser completamente abandonados. Algumas verificações ainda são difíceis para um computador. Exemplo disso é validar layout. Já existem ferramentas que auxiliam, mas estão longe do ideal e não são muito difundidas.

Entender e praticar TDD corretamente demora um pouquinho e exige algum esforço de aprendizado, mas os benefícios são infinitamente maiores do que as dificuldades.

Alguns especialistas afirmam -- e eu concordo com eles -- que, em certo sentido, TDD pende muito mais para arquitetura do que para checagens de "certo" ou "errado". Alegam eles que o grande benefício está na clareza e simplicidade da arquitetura para facilitar manutenção futura do software.

Para finalizar e responder ao título desse artigo, você deve praticar TDD porque te ajuda a:

1. Testar o sistema de forma automatizada, de modo que os testes possam ser executados novamente a cada mudança.

1. Certificar que o sistema faz o que precisa fazer.

1. Identificar pontos de complexidade que devem ser simplificados.

1. Evitar estragar o sistema ao tentar melhorá-lo.

1. Evitar que erros já ocorridos no passado (_regressions_) reapareçam.


Quero agradecer, em ordem alfabética, ao [Cássio Botaro](http://twitter.com/cassiobotaro), ao [Douglas Andrade](http://twitter.com/douglasandrade), ao [Eduardo Klosowski](https://eduardoklosowski.wordpress.com/) e ao [Pedro Miloski](http://twitter.com/pmiloski) que me ajudaram melhorando e revisando esse texto.