Agile Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Testing Patterns: Four Phase Test

11.29.2010
| 5137 views |
  • submit to reddit

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.

  1. Arrange: set up fixture.
  2. Act: exercise the SUT.
  3. Assert: perform checks on the result.
  4. 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

1. Arrange

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().

2. Act

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.

3. Assert

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.

4. Teardown

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.

Example

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. :)

<?php

class FourPhaseTest extends PHPUnit_Framework_TestCase
{
public function testAddsAnRetrieveAnElement()
{
// arrange
$sut = new ArrayObject();

// act
$sut->offsetSet('key', 'value');
$retrieved = $sut['key'];

// assert
$this->assertEquals('value', $retrieved);

// 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
unset($sut);

}

public function testImplementsCountable()
{
// arrange
$sut = new ArrayObject(array(1, 2, 3));

// Constructor Test: no act phase

// assert
$this->assertEquals(3, count($sut));
}

/**
* @expectedException PHPUnit_Framework_Error_Notice
*/
public function testRemovesAnElement()
{
// arrange
$sut = new ArrayObject(array('value', 'otherValue'));

// act
$sut->offsetUnset(0);
$sut[0];

// assert is declared with the annotation
// the same goes for mock expectations: assert is implicit
}
}
Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)