Agrupando casos de teste no SimpleTest

Aloha,

Primeiramente, obrigado ao pessoal pelas belas palavras de incentivo e tudo mais: é esse tipo de coisa que nos empolga pra continuar trabalhando e estudando duro.

Em nosso post de iniciação no SimpleTest, criamos um caso de teste completo de uma calculadora. Relembrando nossa telinha bonita do caso de teste…

ishot-1

Agora vejamos … e se por exemplo, tivéssemos não apenas uma calculadora em nossa aplicação, mas também uma agenda de compromissos, uma agenda de contatos, um wiki, um mural de recados e … enfim, se nossa aplicação fosse composta por N classes como faríamos para rodar os casos de teste unitários de TODAS AS CLASSES ?

Na TDD, como vimos antes, escrevemos todos os testes antes de começarmos a implementar nossa aplicação em si: quando pensamos “rodar os testes” isso se aplica à todas as classes e funcionalidades que possam gerar erros em nossa aplicação.

Ai você: “Rapaz, vou ter que acessar teste por teste como fiz com a calculadora? Mamãe, eu tenho 253 casos de uso!!!!”

Imagina só se você executar, URL por URL ou comando por comando, 253 casos de uso. =/

Pensando nisso, a SimpleTest disponibiliza um recurso de agrupamento de casos de teste chamado de Test Suite. Esse recurso permite agrupar vários casos de testes numa só classe, facilitando e permitindo a execução desses testes com uma só chamada.

Seria como um script de checagem, onde cada item seria um caso de teste:

Checagem do carro

  1. Checar Óleo
  2. Checar Rodas
  3. Checar Água Carburador
  4. Checar itens de segurança

A test suite funciona exatamente assim, executando cada caso de teste na ordem que você desejar: o “check list” roda e no final você tem todos as falhas que ocorreram, separadas por caso de test e método. :)

Codando!

Bom, vamos supor que vamos ter uma aplicação composta por 5 classes: ClasseA, ClasseB, ClasseC, ClasseD e ClasseE. Cada uma delas tem suas próprias implementações e vamos ter que fazer casos de testes que testem elas individualmente.

Para ilustrar o funcionamento, vamos escrever um método para todas as classes chamado Dizer: esse método deverá receber a uma string e retornar “Estou dizendo ” concatenado com o que você passou como parâmetro. Exemplificando

1
$obj->Dizer('Muito Bacana'); // Estou Dizendo Muito Bacana

Vamos botar esse método em todas as classes apenas para simularmos as passagens e não passagens de teste delas. hehhehe

Bom, partindo do pressuposto que você esteja com o SimpleTest instalado e com nossa estrutura de arquivos do artigo de iniciação à ele, vamos escrever o caso de teste da ClasseA que será salvo dentro da nossa pasta tests e terá o nome de classe_a_test.php.

1
2
3
4
5
6
7
8
9
10
11
require_once('simpletest/autorun.php');
require_once('../classes/classe_a.php');</code>

class TestOfClasseA extends UnitTestCase {

  function testDizer() {
    $classea = new ClasseA();
    $this->assertIdentical($classea->dizer('Muito Bacana'), 'Estou dizendo Muito Bacana');
  }

}

Relembrando um pouco:

  • Fazemos o include do arquito autorun.php para automatizar nossos testes;
  • Fazemos o include da classe que vai ser testada
  • Implementamos a classe de teste iniciando por “Test” afim de que a SimpleTest execute-a automaticamente;
  • Criamos o método testDizer para testar o método dizer da nossa ClasseA;
  • Fazemos um assertIdentical que executará nosso método dizer passando “Muito Bacana” e esperará como resultado algo IDENTICO à “Estou dizendo Muito Bacana”;

Beleza, agora vamos rodar nosso teste …

Imagem14

Como era esperado (espero) nosso teste deu pau porque não escrevemos nossa ClasseA ainda em arquivo algum. Então, vamos escrevê-la e salva-la dentro da pasta classes com o nome de classe_a.php:

1
2
3
4
5
class ClasseA {
  function dizer($frase) {
    return 'Estou dizendo ' . $frase;
  }
}

Agora, vamos rodar novamente nosso teste:

Imagem15

Massa demais! Fizemos novamente um caso de teste. Até ai nenhuma novidade.

Agora o exercício braçal: crie os casos de teste para a ClasseB. ClasseC, ClasseD e ClasseE lembrando que:

  • Classes da aplicação devem ser salvas dentro da pasta classes
  • Classes dos casos de teste dentro da pasta tests

Após a criação delas, faça os testes (jura?) e veja se está tudo ok.

Criando nossa suite de testes

Agora que executamos todos os testes de forma separada, vamos junta-los em nossa suite para faze-los todos de uma vez só.

Vamos criar nossa suite de testes no arquivo all_tests.php e salva-lo na pasta tests junto com todos os nosses casos de uso.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require_once('simpletest/autorun.php');
require_once('classe_a_test.php');
require_once('classe_b_test.php');
require_once('classe_c_test.php');
require_once('classe_d_test.php');
require_once('classe_e_test.php');

class AllTests extends TestSuite {
  function AllTests() {
    $this->TestSuite('All tests');
    $this->addTestCase(new TestOfClasseA());
    $this->addTestCase(new TestOfClasseB());
    $this->addTestCase(new TestOfClasseC());
    $this->addTestCase(new TestOfClasseD());
    $this->addTestCase(new TestOfClasseE());
  }
}

Explicando nosso script:

  • fazemos o include de simpletest/autorun.php para fazer rodar nossos testes
  • fazemos os includes de todas os casos de testes que usaremos na suite
  • criamos uma classe AllTests que extende TestSuite, que por sua vez é a classe que será responsável pelo agrupamento e execução de todos os testes
  • criamos um método AllTests que fará a execução dos casos de teste
  • Nomeamos a suite de testes com o nome All tests com o método $this->TestSuite;
  • Adicionamos então todos os nossos casos de teste com o método $this->addTestCase, instanciando o objeto de cada classe de teste

Agora, basta rodar:

Imagem2

Pronto! Nossa suite agrupou nossos testes (que eram cinco ao todo, cada um com um método de teste) e executou todos de uma vez. A lógica da suíte é você inserir os testes conforme seu contexto e sua necessidade de agrupamento de testes. Para incluir, basta repetir o procedimento já feito no arquivo da suíte:

  • faça o require_once do arquivo php do caso de teste
  • inclua o caso de teste com $this->addTestCase

E se algum teste falha-se ? Vamos fazer o teste da ClasseB falhar. Para isso altere a classe B para para retornar algo que não seja “Estou dizendo ” mas sim “Eu vou dizer “: com isso, nosso teste vai falhar. Após alterar a classeB, rode a suite de testes novamente:

Imagem3

Como pode ser visto, nossa suite de testes dá o erro exetamente onde ele aconteceu: no nosso caso, a suite de testes que chamamos de All tests reportou o erro do caso de teste TestOfClasseB no método testDizer. Maneirasso não ?

Nossa suite mostra todas as falhas por caso de teste. Simples e rápido.

E vamos às considerações

A forma que fizemos a construção da nossa suite de testes está diferente da forma em que o próprio site da SimpleTest (http://www.simpletest.org/en/start-testing.html) mostra em sua página inicial no Quick Start dele, que está diferente do link que deveria ser a mesma explicação mais detalhada, mas não é. Se olharmos o script de test suite do quick start teremos, aplicando ao nosso exemplo, teríamos:

1
2
3
4
5
6
7
8
9
require_once('simpletest/autorun.php');

class AllTests extends TestSuite {
  function AllTests() {
    $this->TestSuite('All tests');
    $this->addFile('classe_a_test.php');
    //... outros casos de teste ...
  }
}

Concerteza é um jeito mais prático: ao invés de fazer-mos os includes dos casos de testes e fazermos os instanciamentos dos mesmos no addTestCase, requerendo mais programação, simplesmente podemos incluir o teste com um addFile. E isso realmente funciona. :) Mas infelizmente não sem algumas adaptações no código.

Se quiser re-escrever sua suite de testes nesse formato, se atente ao fato que o SimpleTest aparentemente trabalha com a suite de testes acessando os caminhos absolutos dos arquivos e se perde quando referenciamos nossos arquivos, mesmo que eles estejam na mesma pasta tests. Para contornar isso, coloque a inclusão do arquivo de classe no caso de testes e a referência ao arquivo do caso de teste na suite com seus caminhos absolutos. Vamos isso na classeA por exemplo:

Na classe_a_test.php teríamos:

1
require_once($_SERVER['DOCUMENT_ROOT'] . '/app_tdd/classes/classe_a.php');

E na nossa suite all_tests.php seria re-escrita como:

1
2
3
4
5
6
7
8
require_once('simpletest/autorun.php');

class AllTests extends TestSuite {
  function AllTests() {
    $this->TestSuite('All tests');
    $this->addFile($_SERVER['DOCUMENT_ROOT'] . '/app_tdd/tests/classe_a_test.php');
  }
}

Espero que tenham gostado e no próximo post falaremos um pouco sobre MockObjetcs. ;)

Até lá.

  • Pingback: Mock Objects no SimpleTest | Léo Hackin 0.2c

  • Mauricio

    Cara, os códigos não estão aparecendo. ;)

  • http://alvarengadaniel.wordpress.com/ Daniel Alvarenga

    No primeiro formato ele deu Fatal error, mas no segundo formato, sem o caminho absoluto, ele rodou bacana. =) Pq deu certo aí o primeiro e aqui não?