Test-Driven Development, ou Desenvolvimento Orientado a Testes em português, conhecido por sua sigla TDD é uma prática de engenharia de software que nunca foi unanimidade e que, passado o hype, tem sido mais criticada do que apoiada nos círculos mais populares.
Pelo título, dá pra perceber que eu acredito que a prática tem seu lugar e tentarei explicar meu ponto de vista nesse texto, passando por definição, pontos positivos e negativos. Esse artigo reflete minha opinião, então não é completamente imparcial, mas tentarei passar por todos os pontos que creio relevantes.
O que é TDD?
Talvez a definição não seja muito óbvia caso não seja familiar com o tema, mas Desenvolvimento Orientado a Testes quer dizer que o processo de escrita de código começa pela escrita dos testes, e somente após os testes estarem escritos (e falhando) que a implementação do código começa. Esse ciclo é chamado "Red-Green-Refactor".
A ideia é que dessa maneira, antes de começar a escrever o código, as pessoas desenvolvedoras procurem descobrir quais são as regras de negócio que o código deve satisfazer, e quais são as expectativas daquele código.
Resolvendo FizzBuzz, um exemplo de TDD em prática
Para ilustrar a ideia, segue um exemplo de solução do problema FizzBuzz. A definição do problema é, para todo inteiro n
entre 1 e 100:
- Se
n
for divisível por 3, retorneFizz
. - Se
n
for divisível por 5, retorneBuzz
. - Se
n
for divisível por 3 e 5 ao mesmo tempo, retorneFizzBuzz
.
Usarei Java nos exemplos mas pessoas familiarizadas com código devem entender a ideia ainda que não todos os detalhes.
Estrutura inicial:
public class FizzBuzz {
public String fizzBuzz(final int numero) {
return "";
}
}
O primeiro passo é decidir a lógica de negócio e escrever nossos testes. Vamos seguir o ciclo Red-Green-Refactor, então o primeiro teste que eu vou escrever é cobrindo a primeira regra, Se n
for divisível por 3, retorne Fizz
.
public class FizzBuzzTest {
@Test
public void fizzBuzz_RetornaFizz_QuandoMultiploDe3() {
final String esperado = "Fizz";
final FizzBuzz fizzBuzz = new FizzBuzz();
final String resposta = fizzBuzz.fizzBuzz(3);
assertThat(resposta, CoreMatchers.equalTo(esperado));
}
}
Ao rodar o teste, encontramos o esperado, o teste falha:
java.lang.AssertionError:
Expected: "Fizz"
but: was ""
Expected :Fizz
Actual :
Como em nosso código sempre retornamos uma String vazia, precisamos implementar o código necessário para que esse teste passe. Atualizando a função fizzBuzz
mostrada anteriormente:
public String fizzBuzz(final int numero) {
if (numero % 3 == 0) {
return "Fizz";
}
return "";
}
E agora, ao rodar o teste novamente, vemos que ele passa. O próximo passo seria fazer um teste para quando n
for divisível por 5 e depois por 3 e 5. Sempre começando por um teste falhando e fazendo ele passar.
Cheque aqui o repositório desse projeto para ver como o código e seus testes ficam.
Pontos positivos
Depois de seguir o processo, podemos tecer opiniões sobre o ciclo. Primeiro vou escrever o que penso ser bom no processo.
Pró #1: Primeiro pensar na regra de negócio antes de escrever código
Ao primeiro pensar nos testes, a pessoa desenvolvendo é obrigada a pensar nas regras de negócio, em quais são os casos complicados e se compreenderam o problema. Isso é positivo pois a função do código é satisfazer as regras de negócio, então começar por elas faz sentido.
Pró #2: TDD incentiva a escrita de código testável
Por primeiro escrever os testes, o código por consequência fica organizado de uma maneira que é mais fácil de testar. Usando o mesmo problema FizzBuzz como exemplo, seria possível escrever apenas uma função:
public void resolveTudoEmUm() {
for (int i = 1; i <= 100; i++) {
String resultado = "";
if (i % 3 == 0) {
resultado += "Fizz";
}
if (i % 5 == 0) {
resultado += "Buzz";
}
System.out.println(resultado);
}
}
Essa função executa corretamente, mas é difícil de testar unitariamente. Lógico que nesse exemplo de problema simples é fácil de verificar manualmente, mas assim que os problemas se tornam mais complexos, os testes manuais ficam menos confiáveis.
Código sem teste é código legado, indepedente da "idade", já falei sobre isso em outro post.
TDD pode ser extremamente útil especialmente quando lidando com problemas complexos e para acostumar a escrever código mais testável.
Pontos negativos
Muito já foi escrito sobre porque não utilizar TDD e eu vou tentar cobrir os que são mais importantes na minha opinião.
Contra #1: O desenvolvimento fica mais lento
Para qualquer um que tente seguir o ciclo TDD, uma das primeiras impressões é que a técnica parece diminuir muito a velocidade de desenvolvimento, e seguir à risca pode significar muitos ciclos entre escrever um teste de cada vez e o código associado.
Eu concordo com essa crítica, mas sugiro uma mitigação: para casos óbvios de vários testes, sinta-se à vontade para escrever todos os testes de uma vez e implementar aos poucos. É muito importante manter um ciclo curto entre os testes para garantir o progresso e que nenhum caso deixe de funcionar, mas isso diminui o número de interrupções no processo.
Contra #2: TDD não é a única maneira de escrever código testável
É possível ter os pontos positivos do TDD - ter código testável e pensar na lógica primeiro - sem ter que seguir o processo do TDD que por vezes é muito baby steps e cheio de interrupções.
Esse é o principal motivo pelo qual eu não uso TDD sempre, mas ainda acho o TDD uma técnica muito útil para aumentar o conhecimento sobre como escrever código testável.
Conclusão
Usar TDD influencia positivamente o código gerado, de modo geral o resultado é mais simples de testar e garantir que as regras de negócio são cumpridas. É também muito útil para aprender como escrever código testável.
Porém é uma técnica que, caso seguida à risca, promove muitas interrupções no fluxo de desenvolvimento, o que pode ser frustrante e diminuir muito a velocidade de desenvolvimento.
Particularmente, eu acho excelente para novos serviços em que as regras de negócio não estão super definidas e também para parear com pessoas com menos experiência em escrever testes e código testável.
Top comments (0)