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 635 posts at DZone. You can read more from them at their website. View Full User Profile

Practical PHP Testing Patterns: Creation Method

01.03.2011
| 5230 views |
  • submit to reddit

In our journey into fixture creation strategies, we are transitioning from a simple Inline Setup to a Delegated Setup: from a bunch of new operators to external Creation Methods. Fixture setup is the process of creating objects for test purposes, being them part of the SUT or an input to feed to it.

The Delegated Setup pattern tries to extract Creation Methods from your tests to reuse code and, less often, the fixture themselves. There is something we can call the lost art of Creation Methods: they are often quickly disregarded to shorten the test, but they affect the readability very much. Extracting a good Creation Method can make the difference between a developer wanting to continue doing Test-Driven Development (since the test gets beautiful) and wanting to ditch it as a loss of time (because of a long test, which breaks often.)

Implementation

A suggestion from Meszaros book is to start by defining intent, and then implement what is necessary for the test to run. This is usually intended for writing production code after the test that exercises it, but it is true for fixture creation code too. So when you need a fixture, call a fictitious method which creates it, and you will effectively define its Api from the point of view of the client instead of the implementor - just like you do with Test-Driven Development for production code.

Another scenario when you need to define a creation method is when an Inline Setup gets too long or complex. Again, try to comment out your fixture setup code for a minute and rewrite what you want to extract as a single method call. Then you will be able to move your old code inside the method you have to create. This second scenario is basically the Extract Method refactoring applied to test code.

Variations

There are different categories in which a Creation Method can fall into, according to its complexity and signature.

  • Parameterized Creation Method: the method takes some parameters, which are used to customize the object returned. If you need to create more than one object with the same Creation Method, parameters to distinguish between them are a must (unless you distinguish between them with object references).
  • Anonymous Creation Method: the parameters for creating the fixture are decided by the method instead of being passed in. This is handy when the object needs this configuration in order to be valid, but we don't really care how its field are filled. For example, you may just need a fresh collection object, without any content in it, or a Domain Model object with uninteresting fields set to a default.
  • Parameterized Anonymous Creation Method: a combination of the previous approaches where both fixed values and test-based ones are used.
  • Named State Reaching Method: the method bring the fixture into a known state, which is cited in the name itself. For example, if we're testing a Collection object we may have a createAFullCollection(array(...)) Creation Method which brings the collection into a state where we can testing removal of objects from it. This kind of methods is the most powerful, since with a few keywords you may summarize the state of the whole fixture.
  • Attachment Method: the method takes as a parameter at least the fixture to be modified. The fixture is created in the test itself for flexibility reasons, but the modification is common enough to warrant a method extraction.

Example

The code sample exercises some native Spl classes (Iterator and SplObjectStorage, which is a collection of objects). The variations of the Creation Method pattern are shown.

<?php
class CreationMethodsTest extends PHPUnit_Framework_TestCase
{
public function testParametrizedCreationMethod()
{
$iterator = $this->createIterator(array('A', 'B'));
// ...
}

/**
* The Fixture created varies with the value of $array
*/
private function createIterator(array $array)
{
return new ArrayIterator($array);
}

public function testAnonymousCreationMethod()
{
$storage = $this->createStorage();

$this->assertTrue($storage instanceof Serializable);
$this->assertTrue($storage instanceof ArrayAccess);
}

/**
* For the purpose of the previous test, we don't care what's inside.
*/
private function createStorage()
{
return new SplObjectStorage();
}

public function testNamedStateReachingMethod()
{
$storage = $this->createFullSplStorage();

foreach ($storage as $object) {
$this->assertTrue($storage->contains($object));
}
}

/**
* To check iteration and ownership, we only need an SplStorage that contains
* *something*. Previous assertions do not rely on the values used in this
* Creation Method.
*/
private function createFullSplStorage()
{
$storage = new SplObjectStorage();
$storage->attach(new stdClass);
$storage->attach(new stdClass);
$storage->attach(new stdClass);
return $storage;
}

public function testAnotherNamedStateReachingMethod()
{
$iterator = new ArrayIterator();
$this->getToTheEndOf($iterator);

$this->assertNull($iterator->current());
}

/**
* This Creation Method brings an Iterator to a known state:
* positioned after its last element.
*/
private function getToTheEndOf(Iterator $iterator)
{
while ($iterator->valid()) {
$iterator->next();
}
}
}
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.)