Blz, galera?

TDDNeste post vou falar um pouco sobre TDD – Test-Driven Development. TDD é um assunto muito rico, portanto, voltarei ao tema em novos posts.

Por enquanto, darei um enfoque maior em como começar a trabalhar com TDD na plataforma .NET.

Conceitos básicos

1) TDD é uma técnica de desenvolvimento de software onde nenhum código de produção é escrito sem antes se escrever um teste para ele.

2) O ciclo do TDD consiste em:

  • Red – escreva um teste automatizado que falhe
  • Green – faça o teste passar
  • Refactor – refatore o código para que ele fique o mais simples possível (elimine duplicação)

3) Meta: código limpo que funciona! Por isso, TDD é mais uma prática de design do que uma prática de testes, porque ele te obriga a pensar em como seu código deve funcionar (e interagir com outro código) antes de escrevê-lo.

Frameworks de teste para .Net

Como requisito para trabalhar com TDD, faz-se necessário o uso de testes automatizados. Para tal, existem disponíveis inúmeros frameworks de testes. Em se tratando de testes de unidade, na plataforma .Net, podemos citar o Visual Studio Unit Testing Framework e o NUnit (uma lista completa pode ser encontrada aqui).

O VS Unit Testing, é óbvio, é um framework nativo do Visual Studio e, portanto, é possível rodar os testes de unidade de dentro do próprio VS ou ainda via linha de comando pelo utilitário MSTest.

O NUnit é um framework open-source, muito leve e bastante popular. Possui uma GUI própria para rodar os testes. Isso significa que você terá de configurar o VS para executar o GUI do NUnit para você. Uma outra alternativa é utilizar uma ferramentas de terceiros que permita rodar os testes feitos com o NUnit, como o excelente Resharper.

Citei esses 2 por serem os mais utilizados atualmente.

Exercício Prático

Vamos fazer um exercício bem simples para praticarmos TDD e entendermos o espírito da coisa. Vou utilizar no exemplo o VS Unit Testing. O exercício é: retornar o N-ésimo número da sequência de Fibonacci.

Relembrando: o N-ésimo número de Fibonacci é obtido pela soma dos dois números anteriores da sequência, sendo que o primeiro número é 0 e o segundo é o 1. Sendo assim, o terceiro é 1 (0 + 1), o quarto é 2 (1 + 1) e assim por diante. Teremos então a sequência: 0, 1, 1, 2, 3, 5, 8, 13, 21, …..

Vamos na base dos “baby steps” (passos de bebê, ou seja, seguir em um ritmo bem lento):

1) Sabemos por definição que Fibonacci(0) = 0. Então vamos escrever um teste que atenda essa especificação:

[TestClass]
public class FibonacciTeste
{
   [TestMethod]
   public void OPrimeiroElementoDaSequenciaDeveSer0()
   {
      Assert.AreEqual(0, Fibonacci.Elemento(0));
   }
}

Neste momento, o teste sequer compila pois a classe Fibonacci ainda não foi criada. Como começamos pelo teste, somos “obrigados” a pensar em como queremos utilizar nossa futura funcionalidade. Em outras palavras, estamos pensando do ponto de vista do cliente da classe.

2) Criamos então a classe Fibonacci e o método Elemento, que recebe a posição do número desejado dentro da sequência. Em seguida, implementamos nosso método Elemento da forma mais simples possível para que o teste passe: ROUBANDO. Sendo assim, simplesmente retornamos 0, já que é isso que o teste espera.

public static class Fibonacci
{
   public static int Elemento(int posicao)
   {
      return 0;
   }
}

Você pode estar exclamando: “Que coisa idiota!”. Fique calmo que já já eu falo mais sobre o “roubar”.

3) Neste ponto, nosso teste está passando (Green) e não há nada que possamos fazer para deixar este código mais limpo (Refactor). É óbvio também que nosso método não está pronto, porque ele só retorna o primeiro elemento (posição 0) da sequência. Seguimos em frente então, escrevendo outra especificação:

[TestMethod]
public void OSegundoElementoDaSequenciaDeveSer1()
{
   Assert.AreEqual(1, Fibonacci.Elemento(1));
}

Ao rodar o teste, ele obviamente falha.

4) Voltamos ao método Elemento para atender nosso teste:

public static int Elemento(int posicao)
{
   if (posicao == 0) return 0;
   return 1;
}

Notem acima que tratamos o 0 com o “if” e retornamos o 1 abaixo. Ao rodar o teste, ele passa (assim como o anterior).

5) Vamos para o teste seguinte:

[TestMethod]
public void OTerceiroElementoDaSequenciaDeveSer1()
{
   Assert.AreEqual(1, Fibonacci.Elemento(2));
}

6) E chegamos em algo como:

public static int Elemento(int posicao)
{
   if (posicao == 0) return 0;
   if (posicao == 1) return 1;
   return 1;
}

Percebam que isso não vai chegar ao fim nunca, certo? Mas lembrem-se que: a partir do 3º elemento (posicao=2), o resultado é obtido pela soma dos 2 números anteriores da sequência. Sendo assim, aquele último ‘1’ seria a soma de 0 e 1 (return 0 + 1). Temos então a oportunidade de generalizar nosso método, uma vez que ele já sabe obter os dois primeiros elementos:

public static int Elemento(int posicao)
{
   if (posicao == 0) return 0;
   if (posicao == 1) return 1;
   return Elemento(posicao-2) + 1;
}

Note que o 0 é um exemplo de Elemento(posicao – 2), uma vez que a “posicao” em que estamos é a 2 (terceiro elemento). Rodamos e vemos que os testes continuam passando.

7) Continuamos refatorando. Sabemos que para obter o 1, fazemos Elemento(posicao – 1):

public static int Elemento(int posicao)
{
   if (posicao == 0) return 0;
   if (posicao == 1) return 1;
   return Elemento(posicao-2) + Elemento(posicao-1);
}

E aí está nosso método prontinho. Todo feito a partir dos testes!

Não se sente seguro ainda? Crie testes para mais alguns elementos da sequência e confira!

Retornando constantes e baby steps

Retornar constantes e baby steps, obviamente, não são regras imaculadas do TDD. Conforme você for adquirindo experiência e segurança em seguir adiante, alguns passos podem ser pulados, inclusive começando por uma implementação mais próxima da solução final.

Esses pulos ainda podem ser mais drásticos, caso estejamos utilizando ferramentas de refactoring, como o já citado Resharper.

Concluindo

TDD é uma técnica que ajuda a melhorar o design da aplicação, o que significa aumento de qualidade da mesma. É uma técnica díficil pois exige disciplina para seguirmos seu ciclo e até mesmo para COMEÇARMOS pelos testes.

Dei um caminho aqui para começar a praticar. Agora basta começar. Uma opção é comparecer a Coding Dojos.

Vale lembrar também que TDD é uma técnica de quase uma década e que serviu como base para conceitos mais recentes (e abrangentes) como ATDD e BDD.

Até a próxima!