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: Test Helper

Practical PHP Testing Patterns: Test Helper

Giorgio Sironi user avatar by
Giorgio Sironi
·
Apr. 28, 11 · Interview
Like (0)
Save
Tweet
Share
2.85K Views

Join the DZone community and get the full member experience.

Join For Free

Duplicated test code should be factored out in a single place, like a Test Utility Method, to avoid rotting and difficulties in its maintenance. Yet sharing methods between Testcase Classes is not trivial.

We have seen a way for sharing method that involves inheritance: the Testcase Superclass pattern. However, there is a very powerful alternative to it which uses composition instead: the Test Helper pattern. A Test Helper is an external class, which does not need to extend PHPUnit base classes, which holds Test Utility Methods free to be used in any other test case after instantiation. The main advantage of a composition-based approach is that you have N blocks (your helpers) that you can compose at will, including or leaving out some of them in each test case.

For example, we initially discovered this pattern for testing CRUD controllers which vary in operations supported: one helper for create operations, one for edit ones, one for deletions, one for reading. Given a controller, you can compose and configure from 0 to 4 helpers, and choose which end-to-end tests automatically perform on it; sometimes you write the tests yourself instead of picking the helper, or don't write them because the operation is not supported (e.g. deleting clients or invoices), omitting the helper.

Implementation

I really prefer instances to static classes, as you probably know. However, you'll never mock out these kinds of objects.

In general, Testcase Classes instance one or more test helper to do their job; usually in the setUp() method. An helper can even hold entire tests: in that case the Testcase Class will define the relevant test*() methods and delegate to it. If the helper needs configuration, it can takes parameters in the constructor. You are free to define the Api as the only requirement for helpers is that their classes must be autoloadable.

An important constructor parameter is usually $this (an instance of PHPUnit_Framework_TestCase), a reference that the helper can store and call for obtaining mocks, matchers and make assertions. It's common that you factor out code in Test Helpers: by injecting $this you can continue to use primitives like $this->any() or $this->assertTrue() without shaking up too much the existing code.

The visibility of the methods called on the Testcase Class by Test Helpers must be of course public, but this is not an issue from PHPUnit 3.5; you may however explicitly set some of your Testcase Class methods as private to hide them from helpers. However, your own private or protected methods become public when extracted on an helper.

Variations

A Test Fixture Registry is a Test Helper that accesses the same fixtures as other tests, like a shared database connection. It's handy when there are many resources like this and a single Testcase Superclass for managing all of them won't cut it.

An Object Mother is a Builder or a Factory for objects used in tests. It produces domain objects with an Api friendly to test code, in order for test to stay readable; it's commonly used across many test classes. In our variant, it may even insert these objects in the database, for functional testing purposes.

Example

The code sample shows you a test case class before and after the introduction of the pattern.

Before the Test Helper extraction, duplicated code is a private method that must be inherited by subclassing.

<?php
class TestHelperBeforeTest extends PHPUnit_Framework_TestCase
{
    /**
     * Our only Test Method, but there could be others.
     */
    public function testArrayRespectsStandardStructure()
    {
        $array = array(
            'code' => 200,
            'content' => '...'
        );
        $this->assertArrayRespectsStandardStructure($array);
    }

    /**
     * This is the method we want to extract: a Custom Assertion.
     * We suppose this code is duplicated in other classes, which are won't 
     * included here for brevity.
     */
    private function assertArrayRespectsStandardStructure(array $array)
    {
        $this->assertTrue(isset($array['code']), 'Missing "code" key.');
        $this->assertTrue(is_numeric($array['code']), '"code" key invalid.');
        $this->assertTrue(isset($array['content']), 'Missing "content" key.');
    }
}

After the pattern's application, this code is in an helper that you can use from anywhere, without inheritance; especially if you already use inheritance to gain a reference to some other utility methods.

<?php
class TestHelperAfterTest extends PHPUnit_Framework_TestCase
{
    /**
     * We create the Test Helper in the setUp() in case other Test Methods
     * that need it are present.
     */
    public function setUp()
    {
        $this->standardsHelper = new StandardsHelper($this);
    }

    public function testArrayRespectsStandardStructure()
    {
        $array = array(
            'code' => 200,
            'content' => '...'
        );
        $this->standardsHelper->assertArrayRespectsStandardStructure($array);
    }
}

class StandardsHelper
{
    /**
     * Composing the Testcase Object is useful because of the access to assert*() functions,
     * but also to getMock(), any() and many other utility methods. They could be reimplemented,
     * but compositions is really much less work.
     * Incidentally, this also demonstrates why it's correct to keep methods private 
     * on the Testcase class: they are preserved from usage "da parte di" Test Helpers.
     */
    public function __construct(PHPUnit_Framework_TestCase $testCase)
    {
        $this->testCase = $testCase;
    }

    /**
     * The Custom Assertion becomes public. Other methods which are only called inside
     * this class can remain private.
     */
    public function assertArrayRespectsStandardStructure(array $array)
    {
        $this->testCase->assertTrue(isset($array['code']), 'Missing "code" key.');
        $this->testCase->assertTrue(is_numeric($array['code']), '"code" key invalid.');
        $this->testCase->assertTrue(isset($array['content']), 'Missing "content" key.');
    }
}
Testing PHP

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Select Multiple Checkboxes in Selenium WebDriver Using Java
  • Strategies for Kubernetes Cluster Administrators: Understanding Pod Scheduling
  • All the Cloud’s a Stage and All the WebAssembly Modules Merely Actors
  • Software Maintenance Models

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: