Os testes unitários visam realizar testes automatizados para suas classes. Esses testes por serem automatizados dão segurança para que se façam alterações sem aquele medo de ter quebrado algo no sistema.

Quem aqui nunca fez uma alteração que arrumou uma coisa e sem querer estragou outra? Os testes unitários atuam justamente nesse ponto, se você faz algo que causa alguma conseqüência inesperada em alguma outra parte do sistema, que as vezes você nem se lembra, os testes vão pegar a falha.

Ferramentas de apoio

Quando participei em 2004 da conferência Extreme Programming Brasil eu era um dos poucos desenvolvedores Delphi lá. Porém os conceitos e provas de conceito apresentados podem ser aplicados a qualquer equipe de desenvolvimento independente de linguagem utilizada. 

Sobre testes unitários foi provado que:

  • Dão segurança, apoio, para que alterações seja realizadas de forma mais dinâmica
  • Dão coragem ao desenvolvedor para encarar as alterações solicitadas
  • Dão base para entrega de um sistema sem bugs
 

As ferramentas que dão suporte a esse tipo de teste inicialmente surgiram para o Java e posteriormente foram “traduzidas” para outras linguagens e ambientes populares como Delphi e .NET. Se em Java temos o JUnit, para Delphi win32 temos o DUnit e para .NET temos o NUnit.

DUnit

Vamos focar o artigo de hoje no DUnit com Delphi 7, visto que a maioria do desenvolvimento em Delphi ainda é feito sobre win32 e com a versão 7 do Delphi. O framework pode ser baixado em http://dunit.sourceforge.net/.

A instalação é simples. Descompacte a o conteúdo do arquivo para alguma pasta e  adicione no library path do Delphi  o caminho <pasta instalação>\DUnit\Source

Exemplo

Os mais puristas em desenvolvimento TDD (Test Driven Development), desenvolvimento diridigo por testes, e XP (Extreme Programming) pregam que primeiramente devemos construir os testes  para determinada classe e rodá-lo. Dessa forma nos certificamos que os testes darão errado no inicio e só então se inicia o desenvolvimento da classe a ser testada. Bem, para quem vem de uma cultura onde “arrastar e soltar” é a “melhor coisa do mundo”, é pra dar um nó na cabeça não é? Como vou testar algo que não está pronto.

Bem, eu não sou purista e sou da opinião que devemos tirar o que há de melhor nisso e particularmente primeiro faço a classe e depois a testo.

Inicie um novo projeto no delphi e adicione uma unit. Nessa unit insira a classe da Listagem 1.

Listagem 1: Classe a ser testada

  TNotas = class
  private
    FNota1: integer;
    FNota2: integer;
    FNota3: integer;
    FNota4: integer;
    procedure SetNota1(const Value: integer);
    procedure SetNota2(const Value: integer);
    procedure SetNota3(const Value: integer);
    procedure SetNota4(const Value: integer);
  public
    property Nota1: integer read FNota1 write SetNota1;
    property Nota2: integer read FNota2 write SetNota2;
    property Nota3: integer read FNota3 write SetNota3;
    property Nota4: integer read FNota4 write SetNota4;
    function Media: Real;
  end;

Implemente o método Media conforme a Listagem 2.

Listagem 2: Método Media

function TNotas.Media: Real;
begin
  Result := (Nota1 + Nota2 + Nota3 + Nota4)/4;
end;

Testando a Classe

Feche a aplicação e inicie um novo projeto, removendo deste o Form1, se criado. Acesse Project -> View Source e ajuste o código do projeto de acordo com a Listagem 3.

Listagem 3 Preparando para utilizar DUnit

uses
  Forms,
  TestFrameWork,
  GuiTestRunner;
 
{$R *.res}
 
begin
  Application.Initialize;
  GUITestRunner.RunRegisteredTests;
end.

Salve o projeto e adicione uma nova Unit, nela vamos escrever o teste necessário, conforme a Listagem 4.

Listagem 4 Testes

uses TestFramework, Model;
 
type
  TNotasTestes = class(TTestCase)
  private
    FNotas: TNotas;
  protected
    procedure SetUp; override;
    procedure TearDown; override;
  published
    procedure TestMedia;
  end;

Observe que para testar uma classe, criamos outra de teste. Embora possa parecer trabalhoso e custoso, os benefícios oferecidos são compensadores. Toda classe de teste deve derivar de TTestCase, que apresenta dois métodos que podem ser sobrecarregados e são muito úteis.

O SetUp e o TearDown. O Setup será chamado antes de cada teste publicado ser executado. Assim, podemos preparar os testes com informações necessárias antes de sua execução. O TearDown, é chamado depois que o teste é realizado, podendo ser utilizado para liberar recursos alocados pelo teste.

Pensando em nosso teste vemos que precisaremos de uma instância de TNotas seja criada e aos o teste seja liberada. Utilizaremos então o SetUp e TearDown para isso.

Implemente o método TestMedia conforme a Listagem 5.

Listagem 5: Codificando o teste

procedure TNotasTestes.TestMedia;
begin
  FNotas.Nota1 := 10;
  FNotas.Nota2 := 10;
  FNotas.Nota3 := 10;
  FNotas.Nota4 := 10;
  CheckEquals(10,FNotas.Media);
end;

O framework DUnit oferece vários métodos que utilizamos para realizar testes, em nosso exemplo utilizamos o CheckEquals. CheckEquals vai verificar a igualdade dos valores passados, havendo diferença o teste é identificado como falho.

Antes de executarmos, é preciso registrar o teste no framework, isso é feito na seção initialization, veja a Listagem 6.

Listagem 6: Registrando o teste

initialization
  TestFramework.RegisterTest('Tests Suite', TNotasTestes.Suite);

Execute a aplicação, e dê um ENTER, o teste será executado, veja figura 1.

Testes em execução

Figura 1: Testes em execução

Cometendo um erro

Digamos que precisamos alterar nossa classe de Notas, aumentar mais uma nota, de 4 para 5. E na correria apenas acrescentamos mais uma propriedade Nota5, esquecendo de ajustar a rotina do calculo. Então ajustamos o teste para a nova situação, conforme a Listagem 7.

Listagem 7: Novo teste

procedure TNotasTestes.TestMedia;
begin
  FNotas.Nota1 := 10;
  FNotas.Nota2 := 10;
  FNotas.Nota3 := 10;
  FNotas.Nota4 := 10;
  FNotas.Nota5 := 5;
  CheckEquals(9,FNotas.Media);
end;

Execute o teste, o mesmo irá falhar. Ao detectarmos a falha, podemos então arrumar o cálculo e executar novamente o teste, então ele passará.

Conclusão

Imagine em um sistema complexo como que os testes podem facilitar nossa vida, pegando as falhas que podemos causar. Não é fácil deixar o “jeitão” de fazer as coisas como fazemos e partir para um novo modelo de desenvolvimento, mas com dedicação e boa vontade conseguimos resultados que nunca esperamos. Até mais !