DEV Community

gpiress
gpiress

Posted on

Quando usar TDD

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".

Processo "red-green-development"

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, retorne Fizz.
  • Se n for divisível por 5, retorne Buzz.
  • Se n for divisível por 3 e 5 ao mesmo tempo, retorne FizzBuzz.

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.

Oldest comments (0)