O PHPUnit pode ser estendido de várias formas para facilitar a escrita de testes e personalizar as respostas que você recebe ao executar os testes. Aqui estão pontos de partida comuns para estender o PHPUnit.
Escreva asserções personalizadas e métodos utilitários em uma subclasse abstrata do
PHPUnit_Framework_TestCase
e derive suas classes de caso de teste dessa classe. Essa é uma das formas mais fáceis de estender o PHPUnit.
Ao escrever asserções personalizadas a melhor prática é seguir a mesma forma que as asserções do próprio PHPUnit são implementadas. Como você pode ver em
Exemplo 15.1, o método
assertTrue()
é apenas um empacotador em torno dos métodos
isTrue()
e assertThat()
:
isTrue()
cria um objeto comparador que é passado para
assertThat()
para avaliação.
Exemplo 15.1: Os métodos assertTrue() e isTrue() da classe PHPUnitFramework_Assert
<?php abstract class PHPUnit_Framework_Assert { // ... /** * Asserta que uma condição é verdade. * * @param boolean $condicao * @param string $menssagem * @throws PHPUnit_Framework_AssertionFailedError */ public static function assertTrue($condicao, $mensagem = '') { self::assertThat($condicao, self::isTrue(), $mensagem); } // ... /** * Retorna um objeto equiparador PHPUnit_Framework_Constraint_IsTrue. * * @return PHPUnit_Framework_Constraint_IsTrue * @since Método disponível desde a versão 3.3.0 */ public static function isTrue() { return new PHPUnit_Framework_Constraint_IsTrue; } // ... }?>
Exemplo 15.2 mostra como
PHPUnit_Framework_Constraint_IsTrue
estende a classe base abstrata para objetos comparadores (ou restritores),
PHPUnit_Framework_Constraint
.
Exemplo 15.2: A classe PHPUnit_Framework_Constraint_IsTrue
<?php class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint { /** * Avalia a restrição para o parâmetro $outro. Retorna TRUE se a * restrição é confirmada, FALSE caso contrário. * * @param misto $outro Valor ou objeto a avaliar. * @return bool */ public function matches($outro) { return $outro === TRUE; } /** * Retorna uma representação string da restrição. * * @return string */ public function toString() { return 'é verdade'; } }?>
O esforço de implementar os métodos assertTrue()
e
isTrue()
assim como a classe
PHPUnit_Framework_Constraint_IsTrue
rende o benefício de que assertThat()
automaticamente cuida de avaliar a asserção e escriturar tarefas como contá-las para estatísticas. Além disso, o método isTrue()
pode ser usado como um comparador ao configurar objetos falsos.
Exemplo 15.3
mostra uma implementação simples da interface PHPUnit_Framework_TestListener
.
Exemplo 15.3: Um simples ouvinte de teste
<?php class SimplesOuvinteDeTest implements PHPUnit_Framework_TestListener { public function addError(PHPUnit_Framework_Test $teste, Exception $e, $tempo) { printf("Erro ao executar o teste '%s'.\n", $teste->getName()); } public function addFailure(PHPUnit_Framework_Test $teste, PHPUnit_Framework_AssertionFailedError $e, $tempo) { printf("O Teste '%s' falhou.\n", $teste->getName()); } public function addIncompleteTest(PHPUnit_Framework_Test $teste, Exception $e, $tempo) { printf("O Teste '%s' está incompleto.\n", $teste->getName()); } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { printf("O Teste '%s' é arriscado.\n", $test->getName()); } public function addSkippedTest(PHPUnit_Framework_Test $teste, Exception $e, $tempo) { printf("O Teste '%s' foi pulado.\n", $teste->getName()); } public function startTest(PHPUnit_Framework_Test $teste) { printf("O Teste '%s' iniciou.\n", $teste->getName()); } public function endTest(PHPUnit_Framework_Test $teste, $tempo) { printf("O Teste '%s' terminou.\n", $teste->getName()); } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { printf("A Suíte de Testes '%s' iniciou.\n", $suite->getName()); } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { printf("A Suíte de Testes '%s' terminou.\n", $suite->getName()); } } ?>
Em “Ouvintes de Teste” você pode ver como configurar o PHPUnit para anexar seu ouvinte de teste para a execução do teste.
ocê pode envolver casos de teste ou suítes de teste em uma subclasse do
PHPUnit_Extensions_TestDecorator
e usar o padrão de design do Decorador para realizar algumas ações antes e depois da execução do teste.
O PHPUnit navega com dois decoradores de teste concretos:
PHPUnit_Extensions_RepeatedTest
e
PHPUnit_Extensions_TestSetup
. O formador é usado para executar um teste repetidamente e apenas o conta como bem-sucedido se todas as iterações forem bem-sucedidas. O último foi discutido em Capítulo 4.
Exemplo 15.4
mostra uma versão resumida do decorador de teste PHPUnit_Extensions_RepeatedTest
que ilustra como escrever seus próprios decoradores de teste.
Exemplo 15.4: O Decorador RepeatedTest
<?php require_once 'PHPUnit/Extensions/TestDecorator.php'; class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator { private $repetirVezes = 1; public function __construct(PHPUnit_Framework_Test $teste, $repetirVezes = 1) { parent::__construct($teste); if (is_integer($repetirVezes) && $repetirVezes >= 0) { $this->repetirVezes = $repetirVezes; } } public function count() { return $this->repetirVezes * $this->teste->count(); } public function run(PHPUnit_Framework_TestResult $resultado = NULL) { if ($resultado === NULL) { $resultado = $this->createResult(); } for ($i = 0; $i < $this->repetirVezes && !$resultado->shouldStop(); $i++) { $this->teste->run($resultado); } return $resultado; } } ?>
A interface PHPUnit_Framework_Test
é limitada e fácil de implementar. Você pode escrever uma implementação do
PHPUnit_Framework_Test
que é mais simples que
PHPUnit_Framework_TestCase
e que executa
data-driven tests, por exemplo.
Exemplo 15.5
mostra uma classe de caso de teste guiado por dados que compara valores de um arquivo com Valores Separados por Vírgulas (CSV). Cada linha de tal arquivo parece com
foo;bar
, onde o primeiro valor é o qual esperamos e o segundo é o real.
Exemplo 15.5: Um teste guiado por dados
<?php class GuiadoPorDadosTest implements PHPUnit_Framework_Test { private $linhas; public function __construct($arquivoDados) { $this->linhas = file($arquivoDados); } public function count() { return 1; } public function run(PHPUnit_Framework_TestResult $resultado = NULL) { if ($resultado === NULL) { $resultado = new PHPUnit_Framework_TestResult; } foreach ($this->linhas as $linha) { $resultado->startTest($this); PHP_Timer::start(); $stopTime = NULL; list($esperado, $real) = explode(';', $linha); try { PHPUnit_Framework_Assert::assertEquals( trim($esperado), trim($real) ); } catch (PHPUnit_Framework_AssertionFailedError $e) { $stopTime = PHP_Timer::stop(); $resultado->addFailure($this, $e, $stopTime); } catch (Exception $e) { $stopTime = PHP_Timer::stop(); $resultado->addError($this, $e, $stopTime); } if ($stopTime === NULL) { $stopTime = PHP_Timer::stop(); } $resultado->endTest($this, $stopTime); } return $resultado; } } $teste = new GuiadoPorDadosTest('arquivo_dados.csv'); $resultado = PHPUnit_TextUI_TestRunner::run($teste); ?>
PHPUnit 4.2.0 by Sebastian Bergmann. .F Time: 0 seconds There was 1 failure: 1) GuiadoPorDadosTest Failed asserting that two strings are equal. expected string <bar> difference < x> got string <baz> /home/sb/GuiadoPorDadosTest.php:32 /home/sb/GuiadoPorDadosTest.php:53 FAILURES! Tests: 2, Failures: 1.