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: Testcase Class

12.08.2010
| 4893 views |
  • submit to reddit

Since the test logic, in the context of xUnit frameworks, has to be placed in isolated Test Methods, a Testcase class has to be created to accomodate those methods. The term method has no meaning without a class to contain it.

Thus, the Testcase Class pattern is the second level of organization for your testing code: the first consists of methods, while the third consists of groups or test suites.

Implementation by PHPUnit

Usually, the Testcase Class is instantiated one time for each Test Method to run, to easily ensure isolation. In PHPUnit, this is exactly what happens under the hood, and even when you use the @dataProvider annotation to repeat the execution of a Test Method with different parameters, a new Testcase Object will be created.
According to the xUnit Patterns book, this multiple instantiation is the reason why the pattern is called Testcase Class and not Test Suite. After all, such a class contains multiple tests, right? So it shouldn't be called a Testcase, which is an appropriate name for a single test.

However, if we're using one object of the Testcase Class to execute only one method, and then we throw it away to ensure it does not contaminate the following tests, we can be sure it is a single Testcase.

A final remark about grouping tests in classes is that you can easily harness inheritance for your purposes: if you inherit from another Testcase Class instead of PHPUnit directly, you'll gain Test Methods or helper methods in case it is an abstract class; or even a common setUp(). Inheritance hierarchies are commonly the simplest way to eliminate duplication in test suites.

Note also that subclassing could be dangerous, because you may accidentally overwrite protected members of PHPUnit_Framework_TestCase, due to a naming conflict; moreover, if you extend a Testcase Superclass, which is simply a base class for some of your tests, this name clashing is not so unlikely. To avoid this issues as much as possible, I personally set all my object variables in a Testcase Class as private, in order to be told from PHP if I'm inadverdently clashing with an already defined one (PHP does not allow a protected variable to be overrided by a private one, because the superclass won't be able to see the variable itself.) Apart from this caveat, private class members avoid also cluttering your subclass with the internals of the base Testcae class.

What can go in a single Testcase

There are different patterns involving what to put in a Testcase: the discussion here will remain generic, as these variations are actually independent patterns that will be treated later.

  • per-class Testcase: for each production class My_Class, create a Testcase Class named My_ClassTest, which may live in a different folders hierarchy thanks to autoload. This is the most common approach, and will serve you well for all the unit tests of your application.
  • per-feature Testcase: for each feature (of an object, or of a group of objects) you want to test, create a Testcase class. This solution is adopted when a single Testcase Class won't scale to contain all the Test Methods it requires, or when the System Under Test does not correspond to a class (it may be an end-to-end or functional test, which involves tapping on a whole part of the application.) However, is a single class needs more than a Testcase, you may want to check why it has so many responsibilities and if the Extract Class refactoring will help.
  • per-fixture Testcase: since Test Methods in the same Testcase class can easily share fixtures, while sharing fixtures between different ones may be a code smell, Testcase classes can be built around common fixtures. This is the path of least resistance for test which are fixture-heavy, but if you only want to reuse the logic creating the fixtures, and not the data itself, you may want to think about a Test Helper to create a Standard Fixture.

Examples

The example shows you a Testcase class, even if we have already seen many of them in this series without knowing the pattern's name. It also shows a @dataProvider example, which decouples the Testcase Class methods from the actual Testcase objects which will be create by PHPUnit.

<?php

/**
* Two methods from the Test Method article.
*/
class ArrayObjectTest extends PHPUnit_Framework_TestCase
{
/**
* @expectedException PHPUnit_Framework_Error_Warning
* @expectedExceptionMessage Illegal offset type
*/
public function testDoesNotAllowForANonScalarKeyToBeUsed()
{
$arrayObject = new ArrayObject();
$key = new stdClass;
$arrayObject[$key] = 'value';
}

/**
* Each value of the array is an array of parameters.
*/
public static function invalidKeys()
{
return array(
array(new stdClass),
array(array('foo'))
);
}

/**
* A different instance of this class will be created for each
* test method execution, for a total of 3 instances of this
* Testcase class.
* @dataProvider invalidKeys
*/
public function testDoesNotAllowForANonScalarKeyToBeUsed_MultipleVersion($key)
{
$arrayObject = new ArrayObject();
$this->setExpectedException('PHPUnit_Framework_Error_Warning');
$arrayObject[$key] = 'value';
}
}
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.)