Skip to content
Caution: You are browsing the legacy symfony 1.x part of this website.

Teste de Unidade no seus Modelos

Symfony version
Language

Escrever testes unitários para seus modelos Propel ou Doctrine é bem fácil. Neste tutorial, você vai aprender alguns grandes dicas e melhores praticas para escrever melhores testes para seus modelos.

Configuração de Banco de Dados

Para testar classes modelo do Propel, você precisa de um banco de dados. Você ja tem o que você usa para seu desenvolvimento, mas é sempre bom ter o habito de criar um dedicado para testes.

Como todos os testes rodam no ambiente test, só precisamos editar o arquivo de configuração config/databases.yml e sobrescrever os valores padrões para o ambiente test:

test:
  propel:
    param:
      database:     myproject_test
 
dev:
  # dev configuration
 
all:
  propel:
    class:          sfPropelDatabase
    param:
      datasource:   propel
      phptype:      mysql
      hostspec:     localhost
      database:     myproject
      username:     someuser
      password:     somepa$$word

Nesse caso, nós mudamos apenas o nome do banco de dados, mas você pode também mudar o motor de banco de dados para usar por exemplo o SQLite.

Dados de Teste

Agora que temos uma base dedicada para nossos testes, precisamos dar um jeito para carregar dados para o teste(fixtures) para cada vez que rodamos os testes unitários. Isso é porquê nós queremos colocar na base de dados o mesmo estado para todas as vezes que rodarmos nossos testes.

Isso é bem simples, graças a classe sfData:

$loader = new sfPropelData();
$loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');

O método loadData() recebe um diretório ou um arquivo como primeiro argumento. Normalmente, um diretório fixtures é mais ou menos assim:

test/
  fixtures/
    10_categories.yml
    20_articles.yml
    30_comments.yml

Veja o prefixo numérico no nome de todos os arquivos. Essa é uma forma simples de controlar a ordem de carregamento dos dados. Mais tarde se precisarmos inserir novos arquivos fixture, vai ser fácil pois temos vários números livres entre os existentes:

test/
  fixtures/
    10_categories.yml
    15_must_be_laoded_between_categories_and_articles.yml
    20_articles.yml
    30_comments.yml

Os leitores mais atentos irão notar que colocamos nossas fixtures no diretório test/, enquanto o livro do symfony diz para colocarmos no diretório data/. Essa é uma questão de gosto, mas eu gosto de organizar minhas fixtures nesses dois diretórios porque assim posso categorizar as fixtures em dois grupos diferentes:

  • data/fixtures: com todos os dados iniciais para a aplicação funcionar.
  • test/fixtures: com todos os dados necessários para os testes (unitários e funcionais)

Esse jeito simples funciona muito bem para um pequeno agrupamento de dados de teste, mas quando seu modelo cresce, você tem um monte de fixtures, e o tempo necessário para carregar eles na base de dados torna-se importante. Então, temos que dar um jeito de carregar somente um sub-grupo de dados de teste. E uma forma de fazer isso é sub-categorizando seus dados de teste criando subdiretórios de acordo com os recursos principais do projeto:

test/
  fixtures/
    10_cms/
      10_categories.yml
      20_articles.yml
      30_comments.yml
    20_forum/
      10_threads.yml

Agora, ao invés de carregar o diretório principal fixtures, podemos carregar somente um dos subdiretórios, dependendo de quais classes de modelo queremos testar. Mas na maior parte do tempo, também precisaremos carregar dados compartilhados, como users:

test/
  fixtures/
    00_common/
      10_users.yml
    10_cms/
      10_categories.yml
      20_articles.yml
      30_comments.yml
    20_forum/
      10_threads.yml

Para carregar vários diretórios e arquivos de teste, podemos chamar o método loadData() quantas vezes precisarmos:

// load users and all the CMS data
$loader = new sfPropelData();
$loader->loadData(sfConfig::get('sf_test_dir').'/fixtures/00_common/10_users.yml');
$loader->loadData(sfConfig::get('sf_test_dir').'/fixtures/10_cms');

Assim, carregamos o arquivo fixture 10_users.yml e então todos os arquivos de fixture no diretório 10_cms.

Escrevendo Testes Unitários

Temos uma base dedicada e uma forma de colocar nossa base de dados em um estado conhecido, vamos criar algunas testes unitários para o modelo Article.

Aqui um tipico arquivo de bootstrapping de teste unitário para Propel:

// test/unit/model/ArticlePeerTest.php
include(dirname(__FILE__).'/../../bootstrap/unit.php');
 
$configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'test', true);
 
new sfDatabaseManager($configuration);
 
$loader = new sfPropelData();
$loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');
 
$t = new lime_test(1, new lime_output_color());
 
$t->diag('::retrieveBySlug()');
$article = ArticlePeer::retrieveBySlug('the-best-framework-ever');
$t->is($article->getTitle(), 'The Best Framework Ever', '->retrieveBySlug() returns the article that matches the given slug');

Esse script é praticamente auto-explicável:

  • Como para todo teste unitário, incluímos o arquivo de bootstrapping.

    include(dirname(__FILE__).'/../../bootstrap/unit.php');
  • Criamos um objeto de configuração para o ambiente test e habilitamos o debug:

    $configuration = ProjectConfiguration::getApplicationConfiguration('frontend', 'test', true);

    Isso também inicializa o autloading das classes do Propel.

  • Criamos um gerenciar de banco de dados. Ele inicializa a conexão Propel, carregando o arquivo de configuração databases.yml:

    new sfDatabaseManager($configuration);
  • Carregamos nossos dados de teste, usando a classe sfPropelData:

    $loader = new sfPropelData();
    $loader->loadData(sfConfig::get('sf_test_dir').'/fixtures');
  • Agora que esta tudo pronto, podemos começar a teste nossos objetos de modelo.

Se você não esta acostumado a escrever testes unitários, pode ser intimidante no começo.

Algumas dicas que uso o tempo todo para saber o que eu preciso testar:

  • Teste um método de uma classe por vez
  • Teste que para uma dada entrada, o retorno do método é o que você esperava
  • Leia o código do método e teste todas as regras de negócio que você pode ter.
  • Nunca teste coisas obvias ou coisas que são feitas por outros métodos

Meus arquivos de teste, sempre são estruturados com o mesmo padrão:

// output a message with the method you test (-> for instance methods, and :: for class methods)
$t->diag('->methodName()');
 
// teste uma coisa de cada vez que pode ser expressada com uma única sentença
// a sentença sempre começa com o nome do método
// então um verbo expressando o que deve ser feito, como deve comporta-se, ...etc
$t->is($object->methodName(), 1, '->methodName() returns 1 if you pass no argument');

This work is licensed under the Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 Unported License license.