Chapter 5. Fixtures

One of the most time-consuming parts of writing tests is writing the code to set the world up in a known state and then return it to its original state when the test is complete. This known state is called the fixture of the test.

In Example 2.1, the fixture was simply the array that is stored in the $fixture variable. Most of the time, though, the fixture will be more complex than a simple array, and the amount of code needed to set it up will grow accordingly. The actual content of the test gets lost in the noise of setting up the fixture. This problem gets even worse when you write several tests with similar fixtures. Without some help from the testing framework, we would have to duplicate the code that sets up the fixture for each test we write.

PHPUnit supports sharing the setup code. Before a test method is run, a template method called setUp() is invoked. setUp() is where you create the objects against which you will test. Once the test method has finished running, whether it succeeded or failed, another template method called tearDown() is invoked. tearDown() is where you clean up the objects against which you tested.

We can now refactor Example 2.1 and use setUp() to eliminate the code duplication that we had before. First we declare the instance variable, $fixture, that we are going to use instead of a method-local variable. Then we put the creation of the Array fixture into the setUp() method. Finally, we remove the redundant code from the test methods and use the newly introduced instance variable, $this->fixture, instead of the method-local variable $fixture with the assertEquals() assertion method.

Example 5.1: Using setUp() to create the Array fixture

<?php
require_once 'PHPUnit2/Framework/TestCase.php';
 
class ArrayTest extends PHPUnit2_Framework_TestCase {
    protected $fixture;
 
    protected function setUp() {
        // Create the Array fixture.
        $this->fixture = Array();
    }
 
    public function testNewArrayIsEmpty() {
        // Assert that the size of the Array fixture is 0.
        $this->assertEquals(0, sizeof($this->fixture));
    }
 
    public function testArrayContainsAnElement() {
        // Add an element to the Array fixture.
        $this->fixture[] = 'Element';
 
        // Assert that the size of the Array fixture is 1.
        $this->assertEquals(1, sizeof($this->fixture));
    }
}
?>

setUp() and tearDown() will be called once for each test method run. While it might seem frugal to only run these methods once for all the test methods in a test-case class, doing so would make it hard to write tests that are completely independent of each other.

Not only are setUp() and tearDown() run once for each test method, but the test methods are run in fresh instances of the test-case class (see Chapter 13).

More setUp() than tearDown()

setUp() and tearDown() are nicely symmetrical in theory but not in practice. In practice, you only need to implement tearDown() if you have allocated external resources like files or sockets in setUp(). If your setUp() just creates plain PHP objects, you can generally ignore tearDown(). However, if you create many objects in your setUp(), you might want to unset() the variables pointing to those objects in your tearDown() so they can be garbage collected. The garbage collection of test case objects in not predictable.

Variations

What happens when you have two tests with slightly different setups? There are two possibilities:

  • If the setUp() code differs only slightly, move the code that differs from the setUp() code to the test method.

  • If you really have a different setUp(), you need a different test-case class. Name the class after the difference in the setup.

Suite-Level Setup

PHPUnit does not provide convenient support for suite-level setup. There are few good reasons to share fixtures between tests, but in most cases the need to share a fixture between tests stems from an unresolved design problem.

A good example of a fixture that makes sense to share across several tests is a database connection: you log into the database once and reuse the database connection instead of creating a new connection for each test. This makes your tests run faster. To do this, write your database tests in a test-case class named DatabaseTests and wrap the test suite in a TestSetup decorator object that overrides setUp() to open the database connection and tearDown() to close the connection, as shown in Example 5.2. You can run the tests from DatabaseTests through the DatabaseTestSetup decorator by invoking, for instance, PHPUnit's command-line test runner with phpunit DatabaseTestSetup.

Example 5.2: Writing a suite-level setup decorator

<?php
require_once 'PHPUnit2/Framework/TestSuite.php';
require_once 'PHPUnit2/Extensions/TestSetup.php';
 
class DatabaseTestSetup extends PHPUnit2_Extensions_TestSetup {
    protected $connection = NULL;
 
    protected function setUp() {
        $this->connection = new PDO(
          'mysql:host=wopr;dbname=test',
          'root',
          ''
        );
    }
 
    protected function tearDown() {
        $this->connection = NULL;
    }
 
    public static function suite() {
        return new DatabaseTestSetup(
          new PHPUnit2_Framework_TestSuite('DatabaseTests')
        );
    }
}
?>

It cannot be emphasized enough that sharing fixtures between tests reduces the value of the tests. The underlying design problem is that objects are too closely bound together. You will achieve better results solving the underlying design problem and then writing tests using stubs (see Chapter 10), than by creating dependencies between tests at runtime and ignoring the opportunity to improve your design.