Practical PHP Testing Patterns: Inline Setup
Join the DZone community and get the full member experience.
Join For Freeor if the fixture creation code is:
but rather how to organize your test code. As such, each of the four basic Fixtures pattern map to one or more of this fixture setup patterns.
In the Inline Setup pattern implementations, Test Methods create their fixtures by themselves, with new operators and direct method calls in the System Under Test.
This pattern can be used to create Fresh Fixtures, which are also incidentally Minimal Fixture as you can specialize the inlined creation code in each different test.
Implementation
According to Meszaros book on xUnit Patterns, there are three cases when you would want to use this pattern:
- when the code for creating the fixtures is very simple, and short enough to fit into a Test Method where it should occupy about 1/3 of the lines of code.
- When writing a test for the first time, as at that time we still don't know if the fixture or its setup code will be reused elsewhere; nor we know enough about it to prepare an Api for its creation.
- As the first step of refactoring heavily existing fixture creation code. In this case, if the code is external to the Test Method you inline it in the Test Method itself and start tweaking it by continuosly run the test to ensure it doesn't break. The other methods are not touched at all by this refactoring.
Advantages
This pattern makes the creation code very explicit, by keeping it close to the act and assert phases of each test.
It ensures that the Fixture is Fresh and Minimal, or for the latter it allows you to refactor towards a Minimal Fixture without fearing of influencing other tests.
Disadvantages
This pattern sometimes promotes duplication between different Test Methods.
It also doesn't scale larger than for unit tests (functional/acceptance). If you have to setup a whole application, you won't be able to do it with a couple of new operators or without duplicating many lines of code.
It usually lacks clarity when the arrange phase of the test becomes longer than a few lines of code.
Why moving away from this pattern
Basically, if the logic gets more and more complex, we should move away this fixture setup code to preserve our tests readability and brevity.
A particular case consists in trying to make sense of Obscure Tests. It may be the case that these tests outsource their fixture creation too much, to the point of simply calling a createFixture() method as their initial, arrange phase.
These tests would then be incomprehensible because we cannot see the input we put in the System Under Test: the fixture setup code has been effectively factored out, but we lack knowledge of it and our tests are mere code exercises and no more a kind of documentation. To fix Obscure Tests which derive their issues from fixture setup external to the Test Method, inline that code and see if you can make it better without extracting it from the Test Method itself.
You will often find out that the Standard Fixture simplifies its creation when it becomes a Minimal one, and that you can favor the addition of this new, diffrent code to the reuse of an already existing Standard Fixture.
Examples
The sample code, a Testcase Class, shows two examples of Inline Setup. The first test Inline Setup is straightforward - while the second one may have become too long, and will serve for a refactoring in the next article of this series.
<?php
class InlineSetupTest extends PHPUnit_Framework_TestCase
{
/**
* We will use array as our fixtures, and ArrayITerator and other native
* functionalities as SUTs. This is a simple Inline Setup of the input.
*/
public function testInlineSetupOfANumericalArray()
{
// just two lines of 'Arrange' phase, which is the fixture setup
$array = array('A', 'B');
$iterator = new ArrayIterator($array);
$this->assertEquals('A', $iterator->current());
$iterator->next();
$this->assertEquals('B', $iterator->current());
$iterator->next();
$this->assertFalse($iterator->valid());
}
/**
* Here the code becomes longer, and maybe should be extracted.
* However while I was writing this test, I preferred to have all the
* creation logic available here, in order to tweak it as much as
* possible.
* It's not a 100-line method, but what if we had these 5 arrange
* lines copied into several different Test Methods?
*/
public function testInlineSetupOfAnAppendIterator()
{
$array = array('A');
$anotherArray = array('B');
$iterator = new AppendIterator();
$iterator->append(new ArrayIterator($array));
$iterator->append(new ArrayIterator($anotherArray));
$this->assertEquals('A', $iterator->current());
$iterator->next();
$this->assertEquals('B', $iterator->current());
$iterator->next();
$this->assertFalse($iterator->valid());
}
}
Opinions expressed by DZone contributors are their own.
Comments