Twitter
RSS

Test-Driven Development no .NET

Esse post apresenta a técnica de desenvolvimento orientado a testes, que tem como um de seus objetivos antecipar a identificação e correção de falhas durante o desenvolvimento. Test-driven development ou TDD é um novo paradigma("adoro essa palavra") de desenvolvimento onde primeiro são criados os testes e somente depois é escrito o código necessário para passar por eles. Muita gente deve estar se perguntando: "Mas como vou fazer os testes se não tenho o código para testar?". Vamos tentar esclarecer essas dúvidas no decorrer desse post.

Antes de inicar o desenvolvimento utilizando TDD, deve-se ter o entendimento do que é Unit Test. Em termos práticos, Unit Test (Testes Unitários), significa escrever trechos de código que provem verdadeiro todas as categorias em sua aplicação, ou seja, testar se o objetivo do método está sendo atendido, verificar se a rotina não está sensível a um bug. São apenas testes que garantem que os métodos executam sem erros o que se espera deles.

Embora muitos dos desenvolvedores façam testes unitários em seus códigos, geralmente os testes são desenvolvidos/realizados após o código ser concebido e escrito. Como um grande número de desenvolvedores escreve os códigos antes dos testes, os testes acabam sendo viciosos e difícil de fazer muitas variações devido ao curto tempo. Desenvolvimento orientado a testes (TDD) tenta resolver este problema para gerar maior qualidade no código, evitando que se coloque a carroça na frente dos bois, redigindo os testes antes que escrever o código.

Uma das práticas de Extreme Programming (XP), TDD está adquirindo uma forte comunidade em Java, mas muito pouco foi escrito sobre como fazer em .NET. Ainda assim, foi desenvolvido para .NET, o NUnit Testing Framework, um framework de testes baseado no JUnit da comunidade Java, que serve para executar e apresentar o resultado dos testes. Como ele faz isso? O Test Runner do NUnit faz uma busca no código compilado, procurando por atributos que identifiquem as classes e métodos como de teste. Em seguida, através do uso do Reflection, ele executa essas classes e métodos que foram considerados como de testes. Para informar que a classe é uma classe de testes, não é preciso que ela derive de uma classe base de testes, somente é necessário que sejam utilizados os atributos. O NUnit, possui uma variedade de atributos que devem ser utilizados em seus testes unitários. Eles são utilizados para definir test fixtures, test methods, setup e teardown. Veremos para que servem cada um deles logo abaixo.

Obs.: A forma mais prática de instalar NUnit no seu sistema é instalando o pacote Test Driven.Net, da versão mais recente do NUnit, instalará um add-in pro Visual Studio.Net para que você possa executar os testes dentro do ambiente, diretamente. Outra alternativa (por exemplo pra quem não usa VS.Net) é instalar apenas o NUnit executá-lo como um programa independente.

TestFixture

O atributo TestFixture é usado para indicar que a classe contém métodos de teste. Quando esse atributo é incluído na classe em seu projeto, o Test Runner irá procurar por métodos de teste para então executá-los. O código abaixo mostra o uso desse atributo.

namespace ExemploNUnitTest
{
  using System;
  using NUnit.Framework;

  [TestFixture]
  public class ClasseDeTeste
  {
  }
}

A restrição para classes que possuem o atributo TestFixture é que deve ter defino um construtor padrão como public.(Ou nenhum construtor definido o que é a mesma coisa).

Test

O atributo Test é utilizado para indicar que o método é um método de teste e deve ser executado pelo Test Runner. O método deve ser público, não deve retornar nada e não pode receber parâmetros. Caso contrário o teste não será executado. O código abaixo mostra o exemplo desse atributo.

namespace ExemploNUnitTest
{
  using System;
  using NUnit.Framework;

  [TestFixture]
  public class ClasseDeTeste
  {
    [Test]
    public void TesteUm()
    {
      // Faz alguma coisa...
    }
  }
}

Setup & TearDown

Muitas vezes é necessário inicializar ou limpar variáveis antes de começar a execução dos testes, poderia fazer isso criando um método privado com essas rotinas e chamá-los em cada método de teste. Mas, para isso existem os atributos SetUp e TearDown que indicam que um método deverá ser executado antes(SetUp) ou depois(TearDown) de cada método de teste. A utilização mais comum desses atributos é quando é necessário criar objetos dependentes (e.g., database connections, etc). Este exemplo mostra o uso desses atributos.

namespace ExemploNUnitTest
{
  using System;
  using NUnit.Framework;

  [TestFixture]
  public class ClasseDeTeste
  {
    private int _valor;

    [SetUp]
    public void Setup()
    {
      _valor = 5;
    }

    [TearDown]
    public void TearDown()
    {
      _valor = 0;
    }

    [Test]
    public void TesteUm()
    {
       // Faz alguma coisa...
    }
  }
}

Assert Class

Além dos atributos, um importante recurso que o NUnit disponibiliza é a classe Assert. Essa classe oferece uma variedade de métodos estáticos que podem ser usados para verificar se o resultado da execução do método está de acordo com o esperado. A seguir veja um exemplo do uso da classe Assert.

namespace ExemploNUnitTest
{
  using System;
  using NUnit.Framework;

  [TestFixture]
  public class ClasseDeTeste
  {
    [Test]
    public void TesteUm()
    {
      int i = 4;
      Assert.AreEquals( 4, i );
    }
  }
}

O resultado do método de teste do código acima seria Verdadeiro, caso o valor de i fosse diferente de 4 o método seria validado como Falso.

Rodando seus Testes

É muito simples rodar seus testes com o NUnit, ele possui duas aplicações diferentes para execução dos testes, a aplicação Windows GUI e a aplicação console XML. Para rodar os testes com a aplicação Windows GUI, basta rodar a aplicação, abrir o assembly onde residem seus testes e clicar no botão "Run". Veja um exemplo da aplicação Windows GUI na imagem abaixo:

A interface do NUnit é bem simples, ela nos mostra todos os testes, com o seguinte código de cores ao lado de cada um: vermelho se o teste não passou, e verde se passou. Uma barra de progresso à direita dá um resultado geral (ou seja, verde só se todos passaram).

Os testes ainda podem ser executados pela aplicação console através de linhas de comando. Ela também gera um XML com os resultados do teste, possibilitando formatá-los com XSLT e CSS para integra-los no Visual Studio. Porém essa aplicação é mais utilizada para Integração Contínua.

Vou terminar esse post por aqui para não ficar muito extenso, com esse conteúdo já da para ir treinando seus testes com o NUnit. No próximo post pretendo ir mais fundo em TDD, mostrar seu exemplo na prática e algumas dicas de metodologia no desenvolvimento.

Dica de Livro: Test-Driven Development by Example;

Até a próxima...

Comments (4)

Muito bom seu post, continue assim.

Olá Estevam, muito bom seu artigo, estou iniciando estudos para aplicar TDD integrado com o Spring.net e ao ler este post obtive uma boa noção sobre TDD. Porém, fiquei com duas dúvidas: A primeira é se existe testes em métodos que por ventura não sejam void e contenham sobrecarga. A segunda é a respeito da primeira frase de seu artigo, onde você cita que TDD é uma técnica orientada a testes, lendo um post do Giovanni Bassi, ele explica que na verdade esta técnica é composta por especificações, já que nem sempre testes são especificações. Qual sua opinião sobre isso?

Mais uma vez, parabéns.
Abraço.

Este comentário foi removido pelo autor.

Ramon, parabéns pela iniciativa. TDD + Spring.Net é uma combinação muito interessante, vai lhe render bons frutos. Respondendo suas dúvidas: 1 - Métodos marcados com o atributo de teste [TestMethod] (Microsoft UnitTesting) ou [Test] (NUnit), não podem ser estáticos (static), privados (private), não podem ter retorno (void) e não devem ter nenhum parâmetro. 2 - Na época que escrevi esse post TDD era conceituado como Test Driven Development, atualmente TDD é conceituado como Test Driven Design exatamente como afirma o Giovanni Bassi, TDD não é somente focado na validação, mas também na especificação. Pensando primeiramente em requisitos e design quando os testes forem desenvolvidos, naturalmente você escreverá um código mais limpo e sua aplicação terá um melhor Design.
Um Abraço.