Back-End

29 jul, 2009

Agrupando casos de teste no SimpleTest

Publicidade

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.

No primeiro artigo sobre SimpleTest, criamos um caso de teste completo de uma calculadora. Relembrando nossa telinha bonita do caso de teste?

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 a todas as classes e funcionalidades que possam gerar
erros em nossa aplicação.

Aí 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
todas as falhas que ocorreram, separadas por caso de teste 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

$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 a 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.

require_once('simpletest/autorun.php');
require_once('../classes/classe_a.php');

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” a fim 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 IDÊNTICO a “Estou dizendo Muito Bacana”;

Beleza, agora vamos rodar nosso teste …

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

class ClasseA {

function dizer($frase) {
return 'Estou dizendo ' . $frase;
}

}

Agora, vamos rodar novamente nosso teste:

Massa demais! Fizemos novamente um caso de teste. Até aí 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 juntá-los em nossa suite para fazê-los todos de uma vez só.

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

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:

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:

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 como fizemos a construção da nossa suite de testes está diferente da forma como o próprio site da SimpleTest (http://www.simpletest.org/en/start-testing.html) mostra em sua página inicial no Quick Start, 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, aplicando ao nosso exemplo, teríamos:


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 ...
    }
 }

Com certeza é um jeito mais prático: ao invés de fazermos 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 reescrever 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:

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

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

 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! No próximo artigo falaremos um pouco sobre MockObjetcs.

Até lá.