Sumário
- Testes de Unidade
- Introdução ao Test-Driven Development
- Simplicidade e Baby Steps
- TDD e Design de Classes
- Qualidade no Código do Teste
- TDD e a Coesão
- TDD e o Acoplamento
- TDD e o Encapsulamento
- Testes de Integração e TDD
Prefácio
TDD é uma das práticas de desenvolvimento de soware sugeridas por diversas metodologias ágeis, como XP. A ideia é fazer com que o desenvolvedor escreva testes automatizados de maneira constante ao longo do desenvolvimento. Mas, diferentemente do que estamos acostumados, TDD sugere que o desenvolvedor escreva o teste antes mesmo da implementação.
Essa simples inversão no ciclo traz diversos benefícios para o projeto. Baterias de testes tendem a ser maiores, cobrindo mais casos, e garantindo uma maior qualidade externa. Além disso, escrever testes de unidade forcará o desenvolvedor a escrever um código de maior qualidade pois, como veremos ao longo do livro, para escrever bons testes de unidade, o desenvolvedor é obrigado a fazer bom uso de orientação a objetos.
Testes de Unidade
O que é um teste de unidade?
Desenvolvedores, quando pensam em teste de software, geralmente imaginam um teste que cobre o sistema como um todo. Um teste de unidade não se preocupa com todo o sistema; ele está interessado apenas em saber se uma pequena parte do sistema funciona.
Um teste de unidade testa uma única unidade do nosso sistema. Geralmente, em sistemas orientados a objetos, essa unidade é a classe. Em nosso sistema de exemplo, muito provavelmente existem classes como “CarrinhoDeCompras”, “Pedido”, e assim por diante. A ideia é termos baterias de testes de unidade separadas para cada uma dessas classes; cada bateria preocupada apenas com a sua classe.
Testes automatizados são fundamentais para um desenvolvimento de qualidade, e é obrigação de todo desenvolvedor escrevê-los. Sua existência traz diversos benefícios para o software, como o aumento da qualidade e a diminuição de bugs em produção.
Introdução ao Test-Driven Development
Desenvolvedores (ou qualquer outro papel que é executado dentro de uma equipe de software) estão muito acostumados com o “processo tradicional” de desenvolvi mento: primeiro a implementação e depois o teste.
Refletindo sobre o assunto
De maneira mais abstrata, o ciclo que foi repetido ao longo do processo de desenvolvimento da classe acima foi:
- Escrevemos um teste de unidade para uma nova funcionalidade;
- Vimos o teste falhar;
- Implementamos o código mais simples para resolver o problema;
- Vimos o teste passar;
- Melhoramos (refatoramos) nosso código quando necessário.
Esse ciclo de desenvolvimento é conhecido por Test-Driven Development (TDD), ou, Desenvolvimento Guiado pelos Testes. A ideia é simples: o desenvolvedor deve começar a implementação pelo teste e, deve o tempo todo, fazer de tudo para que seu código que simples e com qualidade.
Esse ciclo é também conhecido como ciclo vermelho-verde-refatora (ou red-green-refactor). O primeiro passo é escrever um teste que falha. A cor vermelha representa esse teste falhando. Em seguida, fazemos ele passar (a cor verde representa ele passando). Por m, refatoramos para melhorar o código que escrevemos.
Quais as vantagens?
Muitos praticantes de TDD armam que executar esse ciclo pode ser muito vantajoso para o processo de desenvolvimento. Alguns deles:
- Foco no teste e não na implementação. Ao começar pelo teste, o programador consegue pensar somente no que a classe deve fazer, e esquece por um momento da implementação. Isso o ajuda a pensar em melhores cenários de teste para a classe sob desenvolvimento.
- Código nasce testado. Se o programador pratica o ciclo corretamente, isso então implica em que todo o código de produção escrito possui ao menos um teste de unidade verificando que ele funciona corretamente.
- Simplicidade. Ao buscar pelo código mais simples constantemente, o desenvolvedor acaba por fugir de soluções complexas, comuns em todos os sistemas.O praticante de TDD escreve código que apenas resolve os problemas que es- tão representados por um teste de unidade. Quantas vezes o desenvolvedor não escreve código desnecessariamente complexo?
- Melhor reexão sobre o design da classe. No cenário tradicional, muitas vezes a falta de coesão ou o excesso de acoplamento é causado muitas vezes pelo desenvolvedor que só pensa na implementação e acaba esquecendo como a classe vai funcionar perante o todo. Ao começar pelo teste, o desenvolvedor pensa sobre como sua classe deverá se comportar perante as outras classes do sistema. O teste atua como o primeiro cliente da classe que está sendo escrita. Nele, o desenvolvedor toma decisões como o nome da classe, os seus métodos, parâmetros, tipos de retorno, e etc. No m, todas elas são decisões de design e, quando o desenvolvedor consegue observar com atenção o código do teste, seu design de classes pode crescer muito em qualidade.
Uma pergunta que pode vir a cabeça é: "No modelo tradicional, onde os testes são escritos depois, o desenvolvedor não tem os mesmos benefícios?”
A resposta é sim. Mesmo desenvolvedores que escrevem testes depois podem obter as mesmas vantagens.
Mas há uma diferença que faz toda a diferença: a quantidade de vezes que um programador praticante de TDD recebe feedback sobre esses pontos e a quantidade que um programador que não pratica TDD recebe. O praticante de TDD escreve um pouco de testes, um pouco de implementação e recebe feedback. Isso acontece ao longo do desenvolvimento de maneira frequente. Já um programador que não pratica TDD espera um tempo (às vezes longo demais) para obter o mesmo feedback.
Ao receber feedback desde cedo, o programador pode melhorar o código e corrigir problemas a um custo menor do que o programador que recebeu a mesma mensagem muito tempo depois. Todo programador sabe que alterar o código que ele acabara de escrever é muito mais fácil e rápido do que alterar o código escrito 3 dias atrás.
No m, TDD apenas maximiza a quantidade de feedback sobre o código que está sendo produzido, fazendo o programador perceber os problemas antecipadamente e, por consequência, diminuindo os custos de manutenção e melhorando o código.