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 de
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 no
Exemplo 14.1, o método
assertTrue()
é apenas um invólucro em torno dos métodos
isTrue()
e assertThat()
:
isTrue()
cria um objeto comparador que é passado para
assertThat()
para avaliação.
Exemplo 14.1: Os métodos assertTrue() e isTrue() da classe PHPUnit_Framework_Assert
<?php use PHPUnit\Framework\TestCase; abstract class PHPUnit_Framework_Assert { // ... /** * Assevera que uma condição é verdadeira (true). * * @param boolean $condition * @param string $message * @throws PHPUnit_Framework_AssertionFailedError */ public static function assertTrue($condition, $message = '') { self::assertThat($condition, self::isTrue(), $message); } // ... /** * Retorna um objeto comparador 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; } // ... }?>
O Exemplo 14.2 mostra como
PHPUnit_Framework_Constraint_IsTrue
estende a
classe base abstrata para objetos comparadores (ou objetos-restrições),
PHPUnit_Framework_Constraint
.
Exemplo 14.2: A classe PHPUnit_Framework_Constraint_IsTrue
<?php use PHPUnit\Framework\TestCase; class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint { /** * Avalia a restrição para o parâmetro $other. Retorna true se a * restrição é confirmada, false caso contrário. * * @param mixed $other Valor ou objeto a avaliar. * @return bool */ public function matches($other) { return $other === true; } /** * Retorna uma representação string da restrição. * * @return string */ public function toString() { return 'is true'; } }?>
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 tais como contá-las para
estatísticas. Além disso, o método isTrue()
pode ser
usado como um comparador ao configurar objetos falsificados.
O Exemplo 14.3
mostra uma implementação simples da interface
PHPUnit\Framework\TestListener
.
Exemplo 14.3: Um simples ouvinte de teste
<?php use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestListener; class SimpleTestListener implements TestListener { public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) { printf("Error while running test '%s'.\n", $test->getName()); } public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) { printf("Test '%s' failed.\n", $test->getName()); } public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) { printf("Test '%s' is incomplete.\n", $test->getName()); } public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) { printf("Test '%s' is deemed risky.\n", $test->getName()); } public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) { printf("Test '%s' has been skipped.\n", $test->getName()); } public function startTest(PHPUnit_Framework_Test $test) { printf("Test '%s' started.\n", $test->getName()); } public function endTest(PHPUnit_Framework_Test $test, $time) { printf("Test '%s' ended.\n", $test->getName()); } public function startTestSuite(PHPUnit_Framework_TestSuite $suite) { printf("TestSuite '%s' started.\n", $suite->getName()); } public function endTestSuite(PHPUnit_Framework_TestSuite $suite) { printf("TestSuite '%s' ended.\n", $suite->getName()); } } ?>
Exemplo 14.4
mostra como a subclasse da classe abstrata PHPUnit_Framework_BaseTestListener
,
que permite que você especifique apenas os métodos de interface que
são interessantes para seu caso de uso, ao fornecer implementações vazias
para todos os outros.
Exemplo 14.4: Usando o ouvinte de teste base
<?php use PHPUnit\Framework\TestCase; class ShortTestListener extends PHPUnit_Framework_BaseTestListener { public function endTest(PHPUnit_Framework_Test $test, $time) { printf("Test '%s' ended.\n", $test->getName()); } } ?>
Em “Ouvintes de Teste” você pode ver como configurar o PHPUnit para anexar seu ouvinte de teste para a execução do teste.
Você pode envolver casos de teste ou suítes de teste em uma subclasse de
PHPUnit_Extensions_TestDecorator
e usar o padrão
de projeto Decorator para realizar algumas ações antes e depois da
execução do teste.
O PHPUnit navega com um decorador de teste concreto:
PHPUnit_Extensions_RepeatedTest
. Ele é usado para executar um
teste repetidamente e apenas o conta como bem-sucedido se todas as iterações forem
bem-sucedidas.
O Exemplo 14.5
mostra uma versão resumida do decorador de teste PHPUnit_Extensions_RepeatedTest
que ilustra como escrever seus próprios decoradores de teste.
Exemplo 14.5: O Decorador RepeatedTest
<?php use PHPUnit\Framework\TestCase; require_once 'PHPUnit/Extensions/TestDecorator.php'; class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator { private $timesRepeat = 1; public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1) { parent::__construct($test); if (is_integer($timesRepeat) && $timesRepeat >= 0) { $this->timesRepeat = $timesRepeat; } } public function count() { return $this->timesRepeat * $this->test->count(); } public function run(PHPUnit_Framework_TestResult $result = null) { if ($result === null) { $result = $this->createResult(); } for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) { $this->test->run($result); } return $result; } } ?>
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
testes guiados por dados, por exemplo.
O Exemplo 14.6
mostra uma classe de caso de teste guiado por dados que compara valores de um arquivo
com Valores Separados por Vírgula (CSV). Cada linha de tal arquivo parece com
foo;bar
, onde o primeiro valor é o qual esperamos
e o segundo valor é o real.
Exemplo 14.6: Um teste guiado por dados
<?php use PHPUnit\Framework\TestCase; class DataDrivenTest implements PHPUnit_Framework_Test { private $lines; public function __construct($dataFile) { $this->lines = file($dataFile); } public function count() { return 1; } public function run(PHPUnit_Framework_TestResult $result = null) { if ($result === null) { $result = new PHPUnit_Framework_TestResult; } foreach ($this->lines as $line) { $result->startTest($this); PHP_Timer::start(); $stopTime = null; list($expected, $actual) = explode(';', $line); try { PHPUnit_Framework_Assert::assertEquals( trim($expected), trim($actual) ); } catch (PHPUnit_Framework_AssertionFailedError $e) { $stopTime = PHP_Timer::stop(); $result->addFailure($this, $e, $stopTime); } catch (Exception $e) { $stopTime = PHP_Timer::stop(); $result->addError($this, $e, $stopTime); } if ($stopTime === null) { $stopTime = PHP_Timer::stop(); } $result->endTest($this, $stopTime); } return $result; } } $test = new DataDrivenTest('data_file.csv'); $result = PHPUnit_TextUI_TestRunner::run($test); ?>
PHPUnit 6.5.0 by Sebastian Bergmann and contributors. .F Time: 0 seconds There was 1 failure: 1) DataDrivenTest Failed asserting that two strings are equal. expected string <bar> difference < x> got string <baz> /home/sb/DataDrivenTest.php:32 /home/sb/DataDrivenTest.php:53 FAILURES! Tests: 2, Failures: 1.