Testes Unitários com QTest

 

1. Introdução

Já verificamos em alguns artigos na revista ClubeDelphi facilidades para a geração de testes unitários com o dUnit, nas edições 52 e 54. O dUnit é o framework mais conhecido para geração de testes unitários.

Nos exemplos apresentados do DUnit podemos ter uma classe ou funções "independentes" e sempre possuímos uma classe derivada de um TestCase (Caso de Teste) que faz o trabalho de teste.

Só que pode ocorrer do programador querer criar na própria classe sendo desenvolvida os testes, sem querer separar os processos. Da mesma forma pode ter interesse em montar os testes através de programação orientada a eventos de componentes. Para isto vamos analisar o QTest, um componente extremamente poderoso para o processo de construção de TestSuites.

2. Download e instalação do componente

O QTest está disponível no Site http://www.bigattichouse.com/qtest.zip. Descompactando este arquivo ZIP temos acesso aos packages para instalação. Temos pacotes prontos para instalação do Delphi 4 ao Delphi 7.

Instalado o pacote para Delphi 7 (QTestPakD7.dpk) dois componentes aparecem em uma paleta de componentes chamada "Testing": TTestSuite e TWebConsole. O primeiro componente nós iremos utilizar para montar TestCases, os nossos casos de teste. O segundo permite captar todos os TestCases (da arquitetura do QTest) e gerar o resultado utilizando arquivos XML, HTML ou CSV (arquivos separados por ponto e vírgula para abrir no Excel/OpenOffice).

Não se esqueça de adicionar o path dos fontes do componente QTest no Library Path do Delphi (Tools -> Environment Options -> Library).

4. Aplicação para teste

Construi um teste simples, com três componentes TEdit e um TButton para executar uma soma. Você pode verificar na listagem 1 o código do click do botão para somar.

 

Figura 1 - Aplicação de exemplo

Na Listagem 1 verificamos o código do click de botão para executar a soma:

     

procedure TFormPrincipal.BtnSomaClick(Sender: TObject);

begin

  EdResultado.Text := FloatToStr(

    StrToFloat(EdOp1.Text) +

    StrToFloat(EdOp2.Text));

end;

5. Criação de testes na própria Classe

Utilizando o framework disponibilizado pelo QTest, podemos desenvolver procedimentos de teste nas nossas classes. Para isto temos uma receita de bolo a seguir, para que o QTest identifique estes métodos.  Acrescente na cláusula "uses" a unit TestSuite.

Crie procedimentos que serão métodos de teste. Para isto defina procedures que iniciem por "test_" e tenham um parâmetro do tipo TQTest. Importante: Os métodos devem ser definidos na seção published da classe. Exemplo de declaração de método de teste:

procedure test_somaClick(Test: TQTest);

 

No procedimento criado podemos acessar diversos métodos disponibilizados pela classe TQTest, como o método Check que permite verificar alguma condição (em caso de erro é emitida uma mensagem indicando o erro), ExpectInteger, ExpectString, ExpectMoney, métodos para log, métodos para geração de Screenshots, métodos para timers, e por aí vai.

Veja o exemplo criado no método test_somaClick:

 

procedure TFormPrincipal.test_somaClick(Test: TQTest);

begin

  EdOp1.Text := '1,5';

  EdOp2.Text := '1';

  BtnSomaClick(Test);

  Test.Check('Teste soma 1+1=2!',EdResultado.Text='2');

  Test.ExpectMoney('Teste soma 1+1=2!',2,StrToFloat(EdResultado.Text));

end;

 

6. Criação de testes com componente TTestSuite

O componente TTestSuite permite criar Testes unitários de uma forma bem interessante. O componente possui uma propriedade chamada Tests, onde podemos cadastrar TestCases.

Cada TestCase possui três eventos que podemos trabalhar: OnSetUp (inicialização do teste), OnTearDown (encerramento do teste) e OnTest (que é o evento chamado no momento que você tem que fazer o seu teste). No evento OnTest você tem a disposição novamente um parâmetro do tipo TQTest, com todos os métodos que foram citados na seção anterior.

No evento OnTest também existe um parâmetro onde o desenvolvedor deve indicar o resultado do teste, que segue um dos seguintes: qtUntested, qtPass,qtFail, qtException, qtLog, qtTimer ou qtScreenshot.

Veja o exemplo criado (evento OnTest de um TestCase) que inclusive mostra o exemplo de criação de um Screenshot no teste:

 

procedure TFormPrincipal.TestSuite1TestSomaTest(Test: TQTest; var Result: TQTestResult);

begin

  EdOp1.Text := '2,5';

  EdOp2.Text := '2,5';

  BtnSomaClick(Test);

  Test.ScreenShot(ExtractFilePath(Application.ExeName),'TestSuite1TestSomaTest','detalhes da figura');

  Test.ExpectString('2.5+2.5=5','5',EdResultado.Text);

  if(EdResultado.Text = '5') then

    Result := qtPass

  else

    Result := qtFail;

end;

 

7. Mostrando o resultado dos testes

Para executar os testes temos duas possibilidades. Podemos simplesmente chamar o método "RunQTests" (lembre de colocar o uses TestSuiteConsole). Esta chamada abre a janela para execução dos testes, veja na Figura 2.

 

Figura 2 - Saída do QTest, método RunQTests

Se você implementou algum procedimento na cláusula published iniciando com test_ e com o parâmetro do tipo TQTest e o mesmo não aparece ao executar o procedimento RunQTests, faltou você registar a classe indicando que ela é uma classe testável. Para isto ocorrer você deve implementar a seguinte chamada, como por exemplo:

 

procedure TFormPrincipal.FormCreate(Sender: TObject);

begin

  RegisterTestableObject('TFormPrincipal',self);

end;

 

Desta forma, estamos incluindo o objeto TFormPrincipal como sendo um objeto testável. O QTest vai procurar métodos published que iniciem com test_ e vai adicionar estes no processo de testes unitários.

Agora se você quer gerar o relatório de execução dos testes em formato HTML, XML ou CSV, podemos utilizar o componente TWebConsole. Veja um código de exemplo que faz a geração do resultado dos testes em formato HTML, XML e CSV na mesma pasta do Executável.

 

procedure TFormPrincipal.BtnRodarTestesWebClick(Sender: TObject);

var

  webConsole:TWebConsole;

begin

  webConsole :=TWebConsole.create(nil);

  try

    webConsole.AddAllSuites;

    webConsole.savetofile(ExtractFilePath(Application.ExeName)+ 'saida.csv',wfCSV);

    webConsole.savetofile(ExtractFilePath(Application.ExeName)+ 'saida.xml',wfXML);

    webConsole.savetofile(ExtractFilePath(Application.ExeName)+ 'saida.html',wfHTML);

  finally

    webConsole.free;

  end;

end;

 

O resultado HTML pode ser visualizado na figura 3:

 

Figura 3 - Saída do QTest, formato HTML

8. Conclusões

Com esta dica verificamos como trabalhar com o QTest, um framework para testes unitários.

Por hoje era isto pessoal. Até a próxima!