| Prev | Next |
PHPUnit can be extended in various ways to make the writing of tests easier and customize the feedback you get from running tests. Here are common starting points to extend PHPUnit.
Write utility methods in an abstract subclass of PHPUnit_Framework_TestCase and derive your test case classes from that class. This is one of the easiest ways to extend PHPUnit.
You can wrap test cases or test suites in a subclass of PHPUnit_Extensions_TestDecorator and use the Decorator design pattern to perform some actions before and after the test runs.
PHPUnit ships with two concrete test decorators: PHPUnit_Extensions_RepeatedTest and PHPUnit_Extensions_TestSetup. The former is used to run a test repeatedly and only count it as a success if all iterations are successful. The latter was discussed in Chapter 6.
Example 22.1 shows a cut-down version of the PHPUnit_Extensions_RepeatedTest test decorator that illustrates how to write your own test decorators.
Example 22.1: The RepeatedTest Decorator
<?php
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;
}
}
?>
The PHPUnit_Framework_Test interface is narrow and easy to implement. You can write an implementation of PHPUnit_Framework_Test that is simpler than PHPUnit_Framework_TestCase and that runs data-driven tests, for instance.
Example 22.2 shows a data-driven test-case class that compares values from a file with Comma-Separated Values (CSV). Each line of such a file looks like foo;bar, where the first value is the one we expect and the second value is the actual one.
Example 22.2: A data-driven test
<?php
require_once 'PHPUnit/Framework.php';
require_once 'PHPUnit/Util/Timer.php';
require_once 'PHPUnit/TextUI/TestRunner.php';
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);
PHPUnit_Util_Timer::start();
list($expected, $actual) = explode(';', $line);
try {
PHPUnit_Framework_Assert::assertEquals(trim($expected), trim($actual));
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
$result->addFailure($this, $e, PHPUnit_Util_Timer::stop());
}
catch (Exception $e) {
$result->addError($this, $e, PHPUnit_Util_Timer::stop());
}
$result->endTest($this, PHPUnit_Util_Timer::stop());
}
return $result;
}
}
$test = new DataDrivenTest('data_file.csv');
$result = PHPUnit_TextUI_TestRunner::run($test);
?>
PHPUnit 3.2.10 by Sebastian Bergmann. .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.
By passing a special-purpose PHPUnit_Framework_TestResult object to the run() method, you can change the way tests are run and what result data gets collected.
You do not necessarily need to write a whole subclass of PHPUnit_Framework_TestResult in order to customize it. Most of the time, it will suffice to implement a new PHPUnit_Framework_TestListener (see Table 21.14) and attach it to the PHPUnit_Framework_TestResult object, before running the tests.
Example 22.3 shows a simple implementation of the PHPUnit_Framework_TestListener interface.
Example 22.3: A simple test listener
<?php
require_once 'PHPUnit/Framework.php';
class SimpleTestListener
implements PHPUnit_Framework_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
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()
);
}
}
?>
Example 22.4 shows how to run and observe a test suite.
Example 22.4: Running and observing a test suite
<?php
require_once 'PHPUnit/Framework.php';
require_once 'ArrayTest.php';
require_once 'SimpleTestListener.php';
// Create a test suite that contains the tests
// from the ArrayTest class.
$suite = new PHPUnit_Framework_TestSuite('ArrayTest');
// Create a test result and attach a SimpleTestListener
// object as an observer to it.
$result = new PHPUnit_Framework_TestResult;
$result->addListener(new SimpleTestListener);
// Run the tests.
$suite->run($result);
?>
TestSuite 'ArrayTest' started. Test 'testNewArrayIsEmpty' started. Test 'testNewArrayIsEmpty' ended. Test 'testArrayContainsAnElement' started. Test 'testArrayContainsAnElement' ended. TestSuite 'ArrayTest' ended.
| Prev | Next |
Copyright © 2005-2011 Sebastian Bergmann.