| Prev | Next |
One of the most time-consuming parts of writing tests is writing the code to set the world up in a known state and then return it to its original state when the test is complete. This known state is called the fixture of the test.
In Example 4.1, the fixture
was simply the array that is stored in the $fixture
variable. Most of the time, though, the fixture will be more complex
than a simple array, and the amount of code needed to set it up will
grow accordingly. The actual content of the test gets lost in the noise
of setting up the fixture. This problem gets even worse when you write
several tests with similar fixtures. Without some help from the testing
framework, we would have to duplicate the code that sets up the fixture
for each test we write.
PHPUnit supports sharing the setup code. Before a test method is run, a
template method called setUp() is invoked.
setUp() is where you create the objects against which
you will test. Once the test method has finished running, whether it
succeeded or failed, another template method called
tearDown() is invoked. tearDown()
is where you clean up the objects against which you tested.
We can now refactor Example 4.1 and
use setUp() to eliminate the code duplication that
we had before. First we declare the instance variable,
$fixture, that we are going to use instead of a
method-local variable. Then we put the creation of the
array fixture into the setUp()
method. Finally, we remove the redundant code from the test methods and
use the newly introduced instance variable,
$this->fixture, instead of the method-local variable
$fixture with the assertEquals()
assertion method.
Example 6.1: Using setUp() to create the Array fixture
<?php
require_once 'PHPUnit/Framework.php';
class ArrayTest extends PHPUnit_Framework_TestCase
{
protected $fixture;
protected function setUp()
{
// Create the Array fixture.
$this->fixture = array();
}
public function testNewArrayIsEmpty()
{
// Assert that the size of the Array fixture is 0.
$this->assertEquals(0, sizeof($this->fixture));
}
public function testArrayContainsAnElement()
{
// Add an element to the Array fixture.
$this->fixture[] = 'Element';
// Assert that the size of the Array fixture is 1.
$this->assertEquals(1, sizeof($this->fixture));
}
}
?>
setUp() and tearDown() will be
called once for each test method run. While it might seem frugal to
only run these methods once for all the test methods in a test case
class, doing so would make it hard to write tests that are completely
independent of each other.
Not only are setUp() and tearDown()
run once for each test method, but the test methods are run on fresh
instances of the test case class.
setUp() and tearDown() are nicely
symmetrical in theory but not in practice. In practice, you only need
to implement tearDown() if you have allocated
external resources like files or sockets in setUp().
If your setUp() just creates plain PHP objects, you
can generally ignore tearDown(). However, if you
create many objects in your setUp(), you might want
to unset() the variables pointing to those objects
in your tearDown() so they can be garbage collected.
The garbage collection of test case objects is not predictable.
What happens when you have two tests with slightly different setups? There are two possibilities:
If the setUp() code differs only slightly, move
the code that differs from the setUp() code to
the test method.
If you really have a different setUp(), you need
a different test case class. Name the class after the difference in
the setup.
There are few good reasons to share fixtures between tests, but in most cases the need to share a fixture between tests stems from an unresolved design problem.
A good example of a fixture that makes sense to share across several tests is a database connection: you log into the database once and reuse the database connection instead of creating a new connection for each test. This makes your tests run faster.
Example 6.2
uses the setUp() and tearDown()
template methods of the PHPUnit_Framework_TestSuite
class (see the section called “Using the TestSuite Class”) to
connect to the database before the test suite's first test and to
disconnect from the database after the last test of the test suite,
respectively. The $sharedFixture attribute of an
PHPUnit_Framework_TestSuite object is available in
the object's aggregated PHPUnit_Framework_TestSuite
and PHPUnit_Framework_TestCase objects.
Example 6.2: Sharing fixture between the tests of a test suite
<?php
require_once 'PHPUnit/Framework.php';
class DatabaseTestSuite extends PHPUnit_Framework_TestSuite
{
protected function setUp()
{
$this->sharedFixture = new PDO(
'mysql:host=wopr;dbname=test',
'root',
''
);
}
protected function tearDown()
{
$this->sharedFixture = NULL;
}
}
?>
It cannot be emphasized enough that sharing fixtures between tests reduces the value of the tests. The underlying design problem is that objects are not loosely coupled. You will achieve better results solving the underlying design problem and then writing tests using stubs (see Chapter 11), than by creating dependencies between tests at runtime and ignoring the opportunity to improve your design.
It is hard to test code that uses singletons. The same is true for code that uses global variables. Typically, the code you want to test is coupled strongly with a global variable and you cannot control its creation. An additional problem is the fact that one test's change to a global variable might break another test.
In PHP, global variables work like this:
A global variable $foo = 'bar'; is stored as $GLOBALS['foo'] = 'bar';.
The $GLOBALS variable is a so-called super-global variable.
Super-global variables are built-in variables that are always available in all scopes.
In the scope of a function or method, you may access the global variable $foo by either directly accessing $GLOBALS['foo'] or by using global $foo; to create a local variable with a reference to the global variable.
By default, PHPUnit runs your tests in a way where even changes to global
and super-global variables ($GLOBALS,
$_ENV, $_POST,
$_GET, $_COOKIE,
$_SERVER, $_FILES,
$_REQUEST) do not affect other tests.
The implementation of the backup and restore operations for the
global and super-global variables uses serialize() and
unserialize(). Objects of some classes that are
provided by PHP itself, such as PDO, cannot be
serialized and the backup operation will break when such an object is
stored in the $GLOBALS array, for instance.
The backup and restore operations for the global and super-global variables can be completely disabled for all tests of a test case class like this
class MyTest extends PHPUnit_Framework_TestCase
{
protected $backupGlobals = FALSE;
// ...
}
Please note that setting the $backupGlobals attribute
inside the setUp() method, for instance, has no effect.
| Prev | Next |
assertArrayHasKey()assertClassHasAttribute()assertClassHasStaticAttribute()assertContains()assertContainsOnly()assertEqualXMLStructure()assertEquals()assertFalse()assertFileEquals()assertFileExists()assertGreaterThan()assertGreaterThanOrEqual()assertLessThan()assertLessThanOrEqual()assertNotNull()assertObjectHasAttribute()assertRegExp()assertSame()assertSelectCount()assertSelectEquals()assertSelectRegExp()assertStringEqualsFile()assertTag()assertThat()assertTrue()assertType()assertXmlFileEqualsXmlFile()assertXmlStringEqualsXmlFile()assertXmlStringEqualsXmlString()Copyright © 2005-2009 Sebastian Bergmann.