DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. Practical PHP Testing Patterns: Setup Decorator

Practical PHP Testing Patterns: Setup Decorator

Giorgio Sironi user avatar by
Giorgio Sironi
·
Jan. 19, 11 · Interview
Like (1)
Save
Tweet
Share
2.00K Views

Join the DZone community and get the full member experience.

Join For Free

We have already seen different ways to make the necessary fixture setups before starting the test suite. There are many ways to provide dependencies to the tests however, and this patterns make them agnostic regarding the provenience. In fact, a Setup Decorator implementation can be used to apply Dependency Injection applied to the single Test Cases.

The Setup Decorator pattern exploits the dynamic properties of the Decorator pattern, for instance to reexecute tests in different conditions (in our example, against different databases). In general, a Decorator is a powerful alternative to subclassing. This specialization of the pattern usually works at the Test Suite level: we decorate a Test Suite which does not want to worry about fixture setup, and provide it with its dependencies.

Implementation and variations

There are several variations of this pattern, each corresponding to different choices on how to produce the fixtures. The first two are the most important ones, and will probably serve you well.

  • Abstract Setup Decorator: a class which we only have to subclass and override setUp() or teardown() in order to make a decorator with. PHPUnit_Framework_TestSuite can be used for this purpose, requiring very little boilerplate code.
  • Hard-Coded Setup Decorator: a Decorator class with hard-coded references to the decorated one.
  • Parameterized Setup Decorator: reuse the Decorator and parameterize the Test Object it should run. This way, we can use the same setup code for different Test Objects without them depending on it.
  • Decorated Lazy Setup: an undecorated Testcase Object can construct its own fixture, in case it is not decorated. I would simply use Lazy Setup here.
  • Pushdown Decorators: the Decorator is used at the testcase level, and its setup code is run before each test. Loses the speed.

Example

The sample code shows you a very practical case. You want to reuse the same Testcase Class, but decorated with two different setups, one that provides it an Sqlite connection and one that instead creates a MySQL connection. For simplicity, I'm using a single Testcase Class here, but the code would be exactly the same if a Test Suite would be instatiated instead of MyTestInSuite.

Here is MyTestInSuite.php, the test we want to execute with different connections:

<?php
/**
* This Testcase Class cannot be executed alone: it relies on its Test Suite
* to provide it a PDO connection. In fact, it lacks the Test.php suffix
* so that PHPUnit ignores it by default.
*/
class MyTestInSuite extends PHPUnit_Framework_TestCase
{
/**
* @var PDO
*/
private $connection;

public function setConnection(PDO $pdo)
{
$this->connection = $pdo;
}

public function testSomething()
{
$this->assertTrue($this->connection instanceof PDO);
}

public function testSomethingElse()
{
$this->assertTrue($this->connection instanceof PDO);
}
}

It looks like a class where we have applied Dependency Injection: no class to extend from, simply a setter asking for a dependency, the database connection.

Next, we provide an utility class for our Setup Decorators to extend from, AbstractDatabaseTestSuite.php:

<?php
/**
* An Abstract Setup Decorator: this class can be extended and provides
* and utility method for injecting the database connection into all the tests.
*/
abstract class AbstractDatabaseTestSuite extends PHPUnit_Framework_TestSuite
{
public function injectConnection(PDO $pdo)
{
foreach ($this->tests() as $wrappedTestSuite) {
foreach ($wrappedTestSuite->tests() as $testCaseObject) {
if (method_exists($testCaseObject, 'setConnection')) {
$testCaseObject->setConnection($pdo);
}
}
}
}
}

Then we can define our Setup Decorators, in SqliteSuiteTest.php:

<?php
require_once 'AbstractDatabaseTestSuite.php';
require_once 'MyTestInSuite.php';

/**
* An example of Hard-Coded Setup Decorator. Executes some tests, and provides
* them with an Sqlite in-memory connection.
*/
class SqliteSuiteTest extends AbstractDatabaseTestSuite
{
public static function suite()
{
$suite = new self();
$suite->addTestSuite('MyTestInSuite');
$suite->injectConnection(new PDO('sqlite::memory:'));

return $suite;
}
}

and MysqlSuiteTest.php:

<?php
require_once 'AbstractDatabaseTestSuite.php';
require_once 'MyTestInSuite.php';

/**
* Another example of Hard-Coded Setup Decorator. Executes the same tests as
* SqliteSuiteTest, but provides them with a MySQL connection.
*/
class MysqlSuiteTest extends AbstractDatabaseTestSuite
{
public static function suite()
{
$pdo = new PDO('mysql:host=localhost',
'root',
'');

$suite = new self();
$suite->addTestSuite('MyTestInSuite');
$suite->injectConnection($pdo);
return $suite;
}
}

Now we can execute tests for the different databases separately:

[11:08:48][giorgio@Desmond:~/txt/articles/setupdecorator]$ phpunit SqliteSuiteTest.php 
PHPUnit 3.5.5 by Sebastian Bergmann.

..

Time: 0 seconds, Memory: 4.75Mb

OK (2 tests, 2 assertions)
[11:08:51][giorgio@Desmond:~/txt/articles/setupdecorator]$ phpunit MysqlSuiteTest.php
PHPUnit 3.5.5 by Sebastian Bergmann.

..

Time: 0 seconds, Memory: 4.75Mb

OK (2 tests, 2 assertions)

Or even in the same batch:

[11:08:56][giorgio@Desmond:~/txt/articles/setupdecorator]$ phpunit .
PHPUnit 3.5.5 by Sebastian Bergmann.

....

Time: 0 seconds, Memory: 5.00Mb

OK (4 tests, 4 assertions)

The pattern is called Decorator because orthogonal decorators (which do not call the same setter, in this case) can be composed one into the other without issues. However, if you compose other Decorators you should adapt injectConnection() to become more recursive, since there will many Test Suite objects wrapped the one into the other.

PHP Testing Test suite

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Stress Testing Tutorial: Comprehensive Guide With Best Practices
  • Public Key and Private Key Pairs: Know the Technical Difference
  • Unlock the Power of Terragrunt’s Hierarchy
  • How To Create a Failover Client Using the Hazelcast Viridian Serverless

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: