Practical PHP Testing Patterns: Four Phase Test
The Web Dev Zone is brought to you in partnership with Mendix. Discover how IT departments looking for ways to keep up with demand for business apps has caused a new breed of developers to surface - the Rapid Application Developer.
The Four Phase Test pattern describes how each xUnit-based test follows a structure composed of 4 parts.
In small tests, often this division is reflected with blank lines inserted between the different parts. The need for inserting more blank lines, additional to the ones between the 4 phases, tells you that the test is becoming too long and a method needs to be extracted to preserve readability.
An exception to this pattern occurs sometimes when phases are missing because they are implied or automatized, a situation often true for the 4th phase, Teardown.
Here are the 4 phases of every test you'll ever write.
- Arrange: set up fixture.
- Act: exercise the SUT.
- Assert: perform checks on the result.
- Teardown: reconstitute the state present before the test.
In many case 4) is optional as the teardown of the object structure is automatic the moment they go out of scope, due to garbage collection; in fact Teardown does not have a fancy A- name.
The first three phases are roughly equivalent to the Given-When-Then triple.
What goes in each phase
This setup phase consists in creating the System Under Test, being it an object, an object with injected mocks, an object graph (functional or end-to-end test), external resources, row in the database, etc.
Usually no errors should be raised in this phase; if an operation that can cause an error must be performed, you should probably put it in the act phase.
The use of the setUp() hook can lead to an empty Arrange phase. Actually, it is not empty as much as it is extracted and moved into setUp().
Here you call the SUT's methods. This is the main communication channel in object-oriented applications.
PHP has some magic methods support, so act may involve also access fields or unexistent methods to trigger a magic method we want to test.
As you may have guessed, assert*() methods should be generally placed here. If you make other checks, you can wrap them in custom assert*() methods, but this method extraction is covered by another pattern. Assertions and the calculations on the result that lead up to them (like counting the elements of a collection), are the bread and butter of the Assert phase.
When using mocks, you usually specify before hand how they should be called. This is actually part of the assert phase, even if you do it at the start of the method.
Due to the imperative paradigm of the majority of languages, there's nothing we can do to move it forward. Some frameworks have an explicity verify() call that you can insert as a place holder for this phase, but PHPUnit verifies mocks expectations automatically at the end of the test.
Usually a Teardown phase is only necessary when resource-intensive mechanisms have been instantiated and should be freed explicitly (filesystem, or database connections).
Also many database related patterns act in the teardown hooks. Usually recreating a fresh database for each test is expensive in time (and database licenses sometimes :), and a Shared Fixture is used instead. However, since the database is shared, its state should be reset between tests and the teardown phase can for example delete all rows from the tables.
The sample code shows you several test, annotated with comments describing the division in four phases (just to be clear; I'm not fond of comments). Usually, blank lines are enough to separate the phases, so imagine the tests as the comment were not existing and it was I who was reading them aloud as you select a related line. :)
class FourPhaseTest extends PHPUnit_Framework_TestCase
public function testAddsAnRetrieveAnElement()
$sut = new ArrayObject();
$retrieved = $sut['key'];
// teardown: not necessary, and won't be included in the other tests
// object structures are automatically discarded and you should
// worry to tear down only external resources like connections
public function testImplementsCountable()
$sut = new ArrayObject(array(1, 2, 3));
// Constructor Test: no act phase
* @expectedException PHPUnit_Framework_Error_Notice
public function testRemovesAnElement()
$sut = new ArrayObject(array('value', 'otherValue'));
// assert is declared with the annotation
// the same goes for mock expectations: assert is implicit