O teste é uma etapa indispensável no desenvolvimento de software, pois realizamos diversos testes quando estamos desenvolvendo uma aplicação. Por exemplo, quando implementamos um código em PHP, geralmente usamos os comandos die ou o comando echo para mostrar se nossa classe ou método funcionou corretamente ou ainda quando implementamos estruturas de repetição ou condição à utilização desses comandos são ainda mais comuns. Contudo, essa é uma forma muito amadora de testar nosso código. Um desenvolvedor profissional deverá escrever um código que faça esses testes de forma automatizada, assim, sempre que for necessário, bastará chamar esse código e ele fará tudo sozinho. Esse processo é conhecido como testes automatizados e o aprendizado desse processo será o principal objetivo desse artigo.

Desenvolvimento com Teste

Desenvolvimento Orientado a Teste (TDD) é uma abordagem de desenvolvimento de software que busca descrever o comportamento de um trecho de código a partir de recursos e cenários antes que o código real seja implementado e só depois criar a aplicação sabendo que o comportamento pretendido será alcançado.

O processo de desenvolvimento de uma funcionalidade é o seguinte:

  • Criar um novo teste que descreve um recurso a ser implementado;
  • Executar o novo teste e verificar se ele falhou. Ele deve falhar, uma vez que esse recurso ainda não foi implementado;
  • Implementar de forma simples o novo recurso;
  • Executar todos os testes e certificar-se que todos os testes obtiveram êxito;
  • Melhorar o código e executar os testes novamente para ter certeza que seu recurso ainda está funcionando corretamente.

Depois deverá ser repetido esse processo para cada novo recurso ou melhoria de um recurso existente. Se o recurso implementado for alterado, o teste também deverá ser alterado.

A razão para criar testes antes de implementar um novo recurso está no fato de que assim podemos nos concentrar inicialmente no que queremos alcançar e só depois precisamos nos preocupar em como fazer.

Assim, podemos resumir os principais benefícios dessa abordagem de desenvolvimento em:

  • Primeiro você testa e depois você implementa, dessa forma você identifica os erros antes que de fato eles ocorram;
  • Ganho de qualidade no código implementado e da sua aplicação final;
  • O maior gasto de tempo no desenvolvimento é compensado com uma aplicação final com menos problemas, maximizando a satisfação do cliente;
  • Maior segurança da sua aplicação.

Quando e Como Testar?

A abordagem de teste descrita faz sentido quando pensamos a longo prazo e em projetos relativamente complexos. No entanto, seria um exagero para os projetos mais simples. Existem alguns indicadores que nos mostram quando é apropriado usar essa abordagem de desenvolvimento, são eles:

  • Projeto já está grande e complexo;
  • Os requisitos do projeto estão começando a ficarem complexos. O Projeto cresce constantemente;
  • Projeto será desenvolvido a longo prazo;
  • O custo de uma falha é muito alto.

Configurando o Ambiente de Teste

O Yii oferece suporte a três tipos de testes:

  • Teste Unitário - verifica se uma única unidade de código está funcionando corretamente ou não. No paradigma de programação orientado a objetos, uma unidade de código básica corresponde a uma classe. Nela o teste irá verificar se os métodos funcionam corretamente: para isso ele verá se os resultados retornados são os esperados ou não. Para testes unitários precisamos ter instalado o PHPUnit.
  • Teste Funcional - verifica se uma funcionalidade (por exemplo, o gerenciamento de usuários de um site) está funcionando como o esperado. Dessa forma, o teste funcional está em um nível superior ao unitário, pois, geralmente uma funcionalidade é composta por várias classes. Esse tipo de teste deve ser feito pelo desenvolvedor que já conheça os requisitos do sistema de forma muito profunda. Para testes funcionais precisamos ter instalado o Selenium RC.
  • Teste de Aceitação - verifica cenários a partir da perspectiva de um usuário em um navegador, tendo como objetivo a avaliação da qualidade externa do sistema e a qualidade do seu uso. Dessa forma esse teste só pode ser realizado após a conclusão do software.

Instalando o PHPUnit 3.5

PHPUnit 3.5 é um framework orientado a testes para desenvolvedores PHP. Faz parte da arquitetura xUnit para frameworks de teste unitário e pode ser baixado no site oficial.

Para instalarmos o framework de teste, abra o prompt de comando e acesse o diretório no qual os arquivos de instalação do PHP estão salvos e em seguida execute o arquivo "go-pear.bat" responsavel por iniciar a instalação do PHP Unit.

Aparecerá a mensagem mostrada na Figura 1, bastando apertar ENTER para prosseguir.

Figura 1. Executando o arquivo go-pear.bat

Deverá ser exibida uma lista de PATH's semelhante à Figura 2. Caso algum desses PATH's estejam incorretos reconfigure para os caminhos corretos, caso contrário, aperte ENTER novamente.

Figura 2. Lista de PATHs

Agora você deverá ver uma tela semelhante à Figura 3, onde o instalador irá lhe perguntar se você gostaria de modificar o arquivo de configuração php.ini. Digite Y para sim e confirme.

Figura 3. Atualizando o arquivo php.ini

Após o arquivo php.ini ser alterado, será exibida a próxima tela que exibe uma mensagem informando que a alteração foi executada com sucesso, basta apertar ENTER.

Logo após será pedido para você instalar as variáveis de ambiente, como na Figura 4, digite ENTER para continuar.

Figura 4. Instalação das variáveis de ambiente

Agora será necessário registrar as variáveis de ambiente, para isso vá até o diretório em que foram salvos os arquivos de configuração do PHP e dê um duplo clique no arquivo PEAR_ENV.reg.

Em seguida serão exibidas duas caixas de mensagem uma após a outra, clique em SIM e OK respectivamente. Dessa forma, estamos com o nosso ambiente preparado e agora podemos instalar o PHPUnit, de fato. Para isso execute o comando a seguir:

 pear Chanel-discover pear.phpunit.de

Então deverá ser exibido uma mensagem de sucesso, como mostrado na Figura 5.

Figura 5. Mensagem de sucesso na instalação do PHP Unit

Depois execute o comando a seguir para adicionar o repositório do Symfony:

pear channel-discover pear .symfony-project.com

Agora digite o comando pear update-channels para atualizar a lista de canais utilizados. A tela exibida deve ser a da Figura 6.

Figura 6. Atualiza a lista de canais

Execute o comando pear upgrade-all e por fim o comando a seguir:

pear install phpunit/PHPUnit

O resultado deve ser o mesmo que o da Figura 7.

Figura 7. Fim da instalação

Agora que já instalamos o PHPUnit, vamos ver como instalar o Selenium Remote Control.

Instalando o Selenium Remote Control

Essa ferramenta possibilita a criação de scripts de teste para execução em vários navegadores. Para a sua instalação é necessário que seu computador já possua o JDK (Java Development Kit), que pode ser encontrado em seu site oficial.

Iremos demonstrar a instalação dessa ferramenta na plataforma Windows.

Primeiro você deverá fazer o download do pacote de instalação do Selenium que pode ser encontrado no site da ferramenta. Depois descompacte o arquivo em C:\Selenium.

Em seguida abra o prompt de comando e vá até o diretório C:\Selenium\selenium-server-2.45.0 e observe que o nome do diretório poderá variar de acordo com a versão que você tiver feito o download.

Para executar testes com o Internet Explorer, ainda no prompt de comando digite:

java -jar selenium-server.jar -multiwindow -htmlSuite "*iexplore" "http://www.google.com" "C:\Selenium_Tests\TsTst.html" "C:\Selenium_Tests\results.html"

Lembre-se que você deverá substituir o conteúdo entre aspas pelo seu conteúdo especifico.

Para executar testes com o Internet Explorer, ainda no prompt de comando digite:

java -jar selenium-server.jar -multiwindow -htmlSuite "*chrome" 
  "http://www.google.com" 
  "C:\Selenium_Tests\TsTst.html" 
  "C:\Selenium_Tests\results.html" 

Lembre-se que você deverá substituir o conteúdo entre aspas pelo seu conteúdo especifico.

Note que nos comandos mostrados os resultados da execução dos testes ficarão salvo no arquivo results.html. Dessa forma, você poderá acessar esse arquivo para verificar se algum teste falhou.

Continuando a Configuração do Ambiente de Testes

Ao executarmos o comando yiic webapp no prompt de comando será criada uma nova aplicação Yii. Nela será criado automaticamente um subdiretório chamado tests que possuirá todos os arquivos para realizarmos os nossos testes. Veja a seguir a lista de arquivos:

  • testdrive/
  • protected/ - contendo arquivos da aplicação protegidos
  • tests/ - contendo testes para a aplicação
  • fixtures/ - contendo fixtures do banco de dados
  • functional/ - contendo testes funcionais
  • unit/ - contendo testes unitários
  • report/ - contendo relatórios de cobertura
  • bootstrap.php - o script executado logo no início
  • phpunit.xml - o arquivo de configuração do PHPUnit
  • WebTestCase.php - a classe base para testes funcionais baseados na Web

Nossos códigos de testes estarão concentrados principalmente nos diretórios fixtures, functional e unit e um quarto diretório chamado report é utilizado para o armazenamento dos códigos que são gerados.

A seguir vemos a Listagem 1 que possui os comandos que devem ser executados no console para fazermos testes unitários ou funcionais:

Listagem 1. Comandos para execução de testes unitários e funcionais.



  % cd testdrive/protected/tests
  % phpunit functional/PostTest.php         // executa um teste individual
  % phpunit --verbose functional             // executa todos os testes em 'funcional'
  % phpunit --coverage-html ./report unit

O último comando serve para executar os testes do diretório unit e irá gerar um relatório do código e o salvará no diretório report, que para funcionar necessita da extensão xdebug instalada e ativa.

Na Listagem 2 vemos o conteúdo que deve haver dentro do arquivo bootstrap.php. Esse arquivo é o ponto de partida para a execução de testes no Yii.

Listagem 2. Conteúdo do Arquivo bootstrap.php.



  $yiit='path/to/yii/framework/yiit.php';
  $config=dirname(__FILE__).'/../config/test.php';
  require_once($yiit);
  require_once(dirname(__FILE__).'/WebTestCase.php');
  Yii::createWebApplication($config);

Adicionamos o arquivo yiit.php do framework Yii, que dentre outras coisas inclui as classes necessárias para os testes. Logo após usamos o arquivo teste.php para criar uma instancia da aplicação web. Se abrirmos o arquivo teste.php veremos que ele herda de main.php. Veja na Listagem 3 o código do arquivo teste.php.

Listagem 3. Conteúdo do arquivo teste.php.


  return CMap::mergeArray(
      require(dirname(__FILE__).'/main.php'),
      array(
          'components'=>array(
              'fixture'=>array(
                  'class'=>'system.test.CDbFixtureManager',
              ),
              /* Se você quiser prover uma conexão com o banco de dados de teste deverá descomentar         essa parte
              
              'db'=>array(
                  'connectionString'=>'banco de dados de teste',
              ),
              */
          ),
      )
  );

Para realizarmos testes com aplicações que possuam banco de dados precisamos fornecer os dados do banco, desse modo os testes não irão interferir no desenvolvimento do projeto. Para isso devemos descomentar na Listagem 3 a parte responsável pela configuração do DB e implementar em connectionString o nome da fonte de dados do DB que utilizaremos para teste.

Construindo um Teste Unitário

Para escrevermos um teste unitário devemos implementar uma classe que estenda de CTestCase ou CDbTestCase. Essa classe deverá possuir a nomenclatura classeTestadaTest, dessa forma, se quisermos testar a classe LoginForm como boa prática de programação devemos implementar um classe de teste unitário chamado LoginFormTest. No caso de trabalharmos com testes unitários genéricos devemos estender a classe CTestCase, mas se estivermos trabalhando com active record devemos estendar a classe CDbTestCase.

O arquivo dessa classe de teste deverá ser salvo na pasta protected/tests/unit. O conteúdo desta deve conter vários métodos de testes com o seguinte padrão de nomenclatura testeMetodo, onde método se refere ao nome do método da classe que está sendo testada.

Na Listagem 4 mostraremos como implementar uma classe de teste unitário de modelo active record. Veja que criamos uma classe para testar a classe do modelo Comment (comentário) no exemplo de um blog. Inicialmente criamos a classe CommentTest e armazenamos como protected/tests/unit/CommentTest.php:

Listagem 4. Implementação de uma classe de teste unitário.


  class CommentTest extends CDbTestCase
  {
      public $fixtures=array(
          'posts'=>'Post',
          'comments'=>'Comment',
      );
   
      ...... \\ outros métodos
  }

Agora vamos implementar o método testApprove que irá ser responsável por testar o método approve da classe Comment. A implementação do método é bem simples: inicialmente será inserido um comentário que ficará com status de pendente, depois será verificado que o comentário está pendente ao ser retornado do BD; e por fim, será invocado o método approve e será feita a verificação do status novamente, para saber se ele já está com o status aprovado, como deveria. Veja a Listagem 5.

Listagem 5. Implementação do método testApprove.


  public function testApprove()
  {
      // adiciona um novo comentário com status pendente
      $comment=new Comment;
      $comment->setAttributes(array(
          'content'=>'comment 1',
          'status'=>Comment::STATUS_PENDING,
          'createTime'=>time(),
          'author'=>'me',
          'email'=>'me@devmedia.com',
          'postId'=>$this->posts['sample1']['id'],
      ),false);
      $this->assertTrue($comment->save(false));
   
      // verifica o estado do comentário 
      $comment=Comment::model()->findByPk($comment->id);
      $this->assertTrue($comment instanceof Comment);
      $this->assertEquals(Comment::STATUS_PENDING,$comment->status);
   
      // chama o método approve() e verifica se o status do comentário está aprovado
      $comment->approve();
      $this->assertEquals(Comment::STATUS_APPROVED,$comment->status);
      $comment=Comment::model()->findByPk($comment->id);
      $this->assertEquals(Comment::STATUS_APPROVED,$comment->status);
  }

Testes Funcionais

Veremos agora os princípios básicos para a construção de um teste funcional no Yii.

Da mesma forma que vimos no teste unitário, um teste funcional deve ser escrito com a nomenclatura classeTestadaTest e toda classe de teste funcional implementada deve estender da classe CwebTestCase. Esta deverá ser salva em um arquivo com o nome classeTestadaTest.php, que deve ser salvo no diretório protected/tests/functional.

Assim como em uma classe de teste unitário, uma classe de teste funcional deve conter vários métodos de testes com o padrão de nomenclatura testeMetodo, onde método se refere ao nome do método da classe que está sendo testada. Dessa forma, se formos testar seu método de Login devemos implementar o método testLogin.

Em cada método deverá conter várias declarações que enviarão comandos ao Selenium RC para que ele interaja com a aplicação que desejamos testar.

Como já vimos a classe de mãe de nossas classes de testes funcionais é a classe WebTestCase, vamos dá uma olhada em seu conteúdo, para isso abra o arquivo WebTestCase.php que foi gerado quando executamos o comando yiic webapp. Veja a classe WebTestCase na Listagem 6.

Listagem 6. Conteúdo da classe WebTestCase.


  define('TEST_BASE_URL','http://localhost/yii/demos/blog/index-test.php/');
   
  class WebTestCase extends CWebTestCase
  {
      /**
       * Define a URL base para aplicações de testes.
       */
      protected function setUp()
      {
          parent::setUp();
          $this->setBrowserUrl(TEST_BASE_URL);
      }
   
      ...... 
  }

Podemos perceber que na classe apresentada está a definição de todas as URLs que deverão ser testadas. Devemos observar se estamos usando na URL de testes o arquivo index-test.php no lugar de index.php. A principal diferença entre eles é que o primeiro utiliza a configuração de aplicação test.php enquanto o segundo utiliza o main.php.

Para testar um exemplo iremos implementar a funcionalidade de postagem no blog que usamos no outro exemplo. Como vimos, o primeiro passo deve ser a implementação de uma classe de teste que estenda de WebTestCase, como na Listagem 7.

Listagem 7. Exemplo de implementação de uma classe teste estendendo de WebTestCase.


  class PostTest extends WebTestCase
  {
      public $fixtures=array(
          'posts'=>'Post',
      );
   
      public function testShow()
      {
          $this->open('post/1');
          // verifica a partir do título do post se o exemplo existe
          $this->assertTextPresent($this->posts['sample1']['title']);
          // verifica se o formulário de comentários existe
          $this->assertTextPresent('Leave a Comment');
      }
   
      ......
  }

Lembre que antes de executar qualquer teste funcional deve-se inicializar o servidor do Selenium Remote Control. A forma mais simples de fazer isso é executando o comando a seguir dentro do diretório de instalação do servidor do Selenium Remote Control.

java -jar selenium-server.jar 

Finalizamos assim esse artigo sobre a execução de testes com o Framework Yii. A partir de agora poderemos desenvolver nossas aplicações de forma a identificar erros em nosso projeto de forma prematura, tornando assim nossas aplicações mais seguras.

Espero que tenham gostado. Até a próxima!

Links

Documentação PHPUnit
https://phpunit.de/manual/current/en/phpunit-book.pdf

Documentação Selenium Remote Control
http://www.seleniumhq.org/docs/