The implementation of PHPUnit is a bit unusual, using techniques that are difficult to maintain in ordinary application code. Understanding how PHPUnit runs your tests can help you write them.
A single test is represented by a PHPUnit2_Framework_Test
object and requires a PHPUnit2_Framework_TestResult
object to be run. The PHPUnit2_Framework_TestResult
object is passed to the PHPUnit2_Framework_Test
object's run()
method, which runs the actual test
method and reports any exceptions to the
PHPUnit2_Framework_TestResult
object. This is an
idiom from the Smalltalk world called Collecting Parameter.
It suggests that when you need to collect results over several methods
(in our case the result of the serveral invocations of the
run()
method for the various tests), you should add a
parameter to the method and pass an object that will collect the results
for you. See the article "JUnit: A
Cook's Tour" by Erich Gamma and Kent Beck [GammaBeck1999]
and "Smalltalk Best Practice Patterns" by Kent Beck [Beck1997].
To further understand how PHPUnit runs your tests, consider the test-case class in Example 13.1.
Example 13.1: The EmptyTest class
<?php
require_once 'PHPUnit2/Framework/TestCase.php';
class EmptyTest extends PHPUnit2_Framework_TestCase {
private $emptyArray = array();
public function testSize() {
$this->assertEquals(0, sizeof($this->emptyArray));
}
public function testIsEmpty() {
$this->assertTrue(empty($this->emptyArray));
}
}
?>
When the test is run, the first thing PHPUnit does is convert the
test class into a PHPUnit2_Framework_Test
object --
here, a PHPUnit2_Framework_TestSuite
containing
two instances of EmptyTest
, as shown in
Figure 13.1.
When the PHPUnit2_Framework_TestSuite
is run, it runs
each of the EmptyTest
s in turn. Each runs its own
setUp()
method, creating a fresh
$emptyArray
for each test, as shown in
Figure 13.2. This way,
if one test modifies the array, the other test will not be affected. Even
changes to global and super-global (like $_ENV
)
variables do not affect other tests.
In short, one test-case class results in a two-level tree of objects
when the tests are run. Each test method works with its own copy of the
objects created by setUp()
. The result is tests that
can run completely independently.
To run the test method itself, PHPUnit uses reflection to find the
method name in the instance variable $name
and invokes
it. This is another idiom, called Pluggable Selector,
that is commonly used in the Smalltalk world. Using a Pluggable Selector
makes the writing of tests simpler, but there is a tradeoff: you cannot
look at the code to decide whether a method is invoked, you have to look
at the data values at runtime.