Saturday, September 26, 2009

Practical testing in php part 4: fixtures

This is the fourth part in the php testing series. You may want to subscribe to the feed to be informed when new posts are available.

In the previous parts, we have explored how to install phpunit and how to write tests which exercise our production code. Also we have learned to use the assertion methods to check the actual results: now we are ready to improve the test code from a refactoring point of view, and to take advantage of phpunit features.
While writing more and more test methods, you can notice that you follow a common pattern, commonly known as arrange-act-assert; this is the main motif of state based testing.
The first operation in a test is usually to set up the system under test, being it an object or a complex object graph; then the methods of the object are called during the act part and some assertions (hopefully not more than one) are done on the results returned from these calls. In some cases, when you have allocated external resources like a fake database, a final cleaning up phase is needed.
What you will actually discover is that often part of the arrange phase and the final cleanup code are shared between test methods: for example in case you are testing a single class, the instantiation of an object is a simple operation you can extract from the test methods. To support this extraction, phpunit (and all xUnit frameworks) provide the setUp() and tearDown() template methods.
These methods are executed respectively before and after every test method: default implementations are provided in PHPUnit_Framework_TestCase with an empty body. You can override this empty methods when useful, to have arrange/cleanup code to be shared between tests in the same test case and prepare a known state before every run. This known state is called a fixture.
Your test case class can go from this:
<?php
class ArrayIteratorTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        $iterator = new ArrayIterator(array('a', 'b', 'c'));
        // act, assert...
    }

    public function testOtherFeature()
    {
        $iterator = new ArrayIterator(array('a', 'b', 'c'));
        // act, assert...
    }
}
to this:
<?php
class ArrayIteratorwithFixtureTest extends PHPUnit_Framework_TestCase
{
    private $_iterator;

    public function setUp()
    {
        $this->_iterator = new ArrayIterator(array('a', 'b', 'c'));
    }

    public function testSomething()
    {
        // act on $this->_iterator, assert...
    }

    public function testOtherFeature()
    {
        // act on $this->_iterator, assert...
    }
}
Observe that, since an object of this class will be created to run the test, you can conserve every variable you want as a private member, and then have a reference to it available in the test method. setUp() usage provides a cleaner and Dry solution, and saves many lines of code when many test methods are needed.

Here is some know-how on using fixtures:
  • usually the tearDown() method should not be provided since the fixture is an object graph and will be garbage-collected after all the tests are executed, or overwritten by the next setUp() call. Thus, the empty body provided by default is often enough.
  • the fixture methods are executed for every test, so the test methods have the same state as a starting point. When more than one fixture is requested, the common practice is to break down the test case, preparing more than one test case class for the system under test; these classes represents different scenarios and together constitutes the overall test suite for this system.
  • sharing a fixture between test cases can be a smell for a bad design, since they are not insulated enough and classes know too much of each other. This cannot be done with setUp() methods however, but there are suite-level setup available in phpunit if you must share a fixture. However, keep in mind that you probably can refactor your classes to improve the maintainability of the application and of its test suite.
  • setUpBeforeClass() and tearDownAfterClass() are two hooks (static methods) which are executed before a test case methods are considered and after the overall process is finished. They are the equivalent of setUp() and tearDown(), but at the test case level instead of the test method one.
  • finally, assertPreConditions() and assertPostConditions() are two methods executed before and after the a test method. They differ from setUp() and tearDown() since they are executed only if the test did not already fail and they can halt the execution with a failing assertion. setUp() and tearDown() should never throw exceptions and they are executed anyway, unconcerned by the current test outcome.
This is all you must know on test fixtures to start experimenting with them. I hope your test code will be much more well written after introducing setUp().
In the next part, we'll explore the annotations that can influence phpunit test runner, like @depends and @dataProvider.

You may want to subscribe to the feed to be informed when new articles in this testing series will be available.

No comments:

ShareThis