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

12.15.2010
| 7575 views |
  • submit to reddit

The Testcase Object pattern, which goes hand in hand with the Testcase Class one, is a specialized Command pattern, applied to the single tests of your suite.

The goal of the Testcase Object is to decouple the Testcase methods definition from the actual tests executed, for example recognizing a @dataProvider annotation as we saw in the related article.

Implementation

Here's how this pattern works in conjunction with Testcase Class:

  • first, the runner someway gathers a list of which classes should be run; how it acquires this list is out of the scope of this pattern, but it's usually simply by selecting all the classes in a particular folder whose name ends with Test (at least by default).
  • Then, it uses the PHP reflection Api to find out which methods are present on those classes. Again, by default (and I don't think it's customizable, at least in PHPUnit) a naming convention is used: the public methods starting with test.
  • An object for each Test Method is instantiated. As explained in the Testcase class article, this is way the whole class is called Testcase: because each instance correspondes to a single Test Method run. In some cases, like in the presence of @dataProvider, multiple Testcase Objects are created for a method.
  • The set of Command objects is now ready, and it is run in sequence. Then the results are ready for displaying or gathering into an XML (or some other fancy format) report.

Part of this logic is in the Test Runner, part of it in the base Testcase Class itself for the sake of encapsulation. For instance, the runner only calls a Template Method run() over the Testcase Object, while this method handles internally setUp() and tearDown().

Trade-offs

When there are many test methods in a single Testcase Class, performance can be altered by this one-object-per-test behavior, so there is a trade-off to make. When you are sure that, by design, different assertions do not influence the System Under Test, for example because it does not have a state, you are free to use multiple assertions in the same test. The goal is to avoid instantiation of a large number of Testcase Objects and respective SUTs, especially in the case the Test Methods are too many. There are other solutions however to reuse the same SUT|Shared Fixture link.

An alternative is to prepare the fixture in the setUpBeforeClass() and tearDownAfterClass() static methods, but this will leave the objects created available only as static fields, which may not suite your taste (at least make them private).

Example

In the code sample, we will see a glimpse of PHPUnit's internals, since the Testcase Object creation is something which is taken care for you and don't have to deal with. Here's a part of the base Testcase Class your Testcase Objects will always be instance of.

<?php
abstract class PHPUnit_Framework_TestCase extends PHPUnit_Framework_Assert implements PHPUnit_Framework_Test, PHPUnit_Framework_SelfDescribing
{
/**
* Constructs a test case with the given name.
*
* @param string $name this is the method to call
* @param array $data this are parameters for the test method, in case of @dataProvider annotation
* @param string $dataName
*/
public function __construct($name = NULL, array $data = array(), $dataName = '')
{
if ($name !== NULL) {
$this->setName($name);
}

$this->data = $data;
$this->dataName = $dataName;
}

/**
* Runs the test case and collects the results in a TestResult object.
* If no TestResult object is passed a new one will be created.
*
* @param PHPUnit_Framework_TestResult $result
* @return PHPUnit_Framework_TestResult
* @throws InvalidArgumentException
*/
public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = $this->createResult();
}

// ...

$this->result = $result;

// ...


// ...
$result->run($this);

// ...

if ($this->useErrorHandler !== NULL) {
$result->convertErrorsToExceptions($oldErrorHandlerSetting);
}

$this->result = NULL;

return $result;
}

/**
* Verifies the mock object expectations. This is called automatically at the end of a test.
*
* @since Method available since Release 3.5.0
*/
protected function verifyMockObjects()
{
foreach ($this->mockObjects as $mockObject) {
$this->numAssertions++;
$mockObject->__phpunit_verify();
$mockObject->__phpunit_cleanup();
}

$this->mockObjects = array();
}

/**
* Returns a mock object for the specified class.
* This is one of the API methods that your Testcase Class inherits. In this case, it is the entry point for the mocking framework embedded in PHPUnit.
*
* @param string $originalClassName
* @param array $methods
* @param array $arguments
* @param string $mockClassName
* @param boolean $callOriginalConstructor
* @param boolean $callOriginalClone
* @param boolean $callAutoload
* @return PHPUnit_Framework_MockObject_MockObject
* @throws InvalidArgumentException
* @since Method available since Release 3.0.0
*/
public function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE)
{
$mockObject = PHPUnit_Framework_MockObject_Generator::getMock(
$originalClassName,
$methods,
$arguments,
$mockClassName,
$callOriginalConstructor,
$callOriginalClone,
$callAutoload
);

$this->mockObjects[] = $mockObject;

return $mockObject;
}

/**
* Returns a builder object to create mock objects using a fluent interface.
*
* @param string $className
* @return PHPUnit_Framework_MockObject_MockBuilder
* @since Method available since Release 3.5.0
*/
public function getMockBuilder($className)
{
return new PHPUnit_Framework_MockObject_MockBuilder(
$this, $className
);
}

/**
* Returns a matcher that matches when the method it is evaluated for
* is executed zero or more times.
* Another API method: this time it serves to provide a matcher for the construction of expectations on mock objects. There are many methods akin to this, like for example once() or never().
*
* @return PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount
* @since Method available since Release 3.0.0
*/
public static function any()
{
return new PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
}

/**
* Hook methods which are by default empty, but can be redefined by your Testcase Class.
* This method is called before the first test of this test class is run.
*
* @since Method available since Release 3.4.0
*/
public static function setUpBeforeClass()
{
}

/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
*
*/
protected function setUp()
{
}

/**
* Tears down the fixture, for example, close a network connection.
* This method is called after a test is executed.
*/
protected function tearDown()
{
}

/**
* This method is called after the last test of this test class is run.
*
* @since Method available since Release 3.4.0
*/
public static function tearDownAfterClass()
{
}
}
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.)