Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Practical PHP Testing Patterns: Named Test Suite

DZone's Guide to

Practical PHP Testing Patterns: Named Test Suite

· Web Dev Zone ·
Free Resource

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

If you are like me and other test-infected developers, you will find more and more test classes hanging around in your suite. It will be critical to find a way to organize them and to run exactly the test you want at a certain moment, with the least possible effort.

The Named Test Suite pattern makes you define a test suite by providing a name, or however a single command to run: the goal is being able to perform these tests as a group in a repeatable way.

Why Named Test Suites?

There are different subsets of test you may run in different times:

  • unit tests: fast, in-memory test, to run during development.
  • Functional tests: bigger tests, also run during development, but less often due to their reduced speed. These tests expose wiring bugs.
  • End-to-end tests: exercise the system as a whole, from the user interface to the database. They are very slow in comparison to unit ones, and they are commonly executed just before publishing your changes.
  • Integration tests: exercise just external tools, for exploratory or regression goals. They are run periodically or after an upgrade of your libraries.

So it's clear that sometimes I want all my unit tests to run (at least the ones on the component I'm working on), while sometimes I want to run the tests that check Doctrine works well with MySQL, or the ones that exercise my user interface, but in doing so require Selenium to be present on my machine.

Each of these groups of tests is a candidate for a Named Test Suite.

Implementation

The concept of running named sets of tests is independent of how do you build them: the *how* is the implementation part.

While some years ago you would have to define boring-to-maintain AllTests classes, with PHPUnit 3.x you have the ability to not only define test suites dynamically and without duplication, but also to intersect them:

phpunit --group groupName

More on this command in the examples at the end of the article.

phpunit --filter testMethodName

is also useful in selecting a particular method, for running Single Test Suites.
Note that PHPUnit provides you with natural test suites: subfolders. Provided that you have defined a bootstrap, you can execute all the tests in a folder simply with

phpunit folder/subfolder/

If you're more a shell guy, you can set up an alias for a phpunit command in order to repeat it later. However aliases aren't portable to other machines through version control.

Thus, the best way to save a complex PHPUnit command somewhere is a phing (or Ant if you prefer) target:

<exec command="phpunit ..." dir="tests/" />

These targets are also useful in a Continuos Integration environment, as you prefer a definition which you can also run on your machine to a long configuration for your specific CI server. It's better telling Jenkins:

phing test

than

phpunit --group ... --log-junit ... --coverage-html ...

when you have to configure the build.

Variations

As you have seen, Named Test Suites can be created at different scales:

An AllTests Suite runs everything you have. It could take long to run - some minutes or some dozens of minutes if you have Selenium-based tests in it.

A Subset Suite excludes slow tests or tests that require particular resources, like a running database server.

A Single Test Suite runs single Test Method of a single Testcase Class.

Examples

Here are some simple Testcase classes, annotated with @group:

<?php
abstract class FakeTest extends PHPUnit_Framework_TestCase
{
public function testNothing() {}
}

/**
* @group unit
* @group domain
*/
class ATest extends FakeTest
{
}

/**
* @group unit
* @group application
*/
class BTest extends FakeTest
{
}

/**
* @group functional
* @group domain
*/
class CTest extends FakeTest
{
}

/**
* @group endtoend
* @group application
*/
class DTest extends FakeTest
{
}

And here is how to run them selectively, in different suites:

List the suites available. Note that some of them overlap, as @group annotations are a "folksonomy and not a taxonomy".

[17:37:01][giorgio@Desmond:~]$ phpunit --list-groups txt/articles/
Available test group(s):
- application
- domain
- endtoend
- functional
- unit

Run a single Named Test Suite:

[17:37:31][giorgio@Desmond:~]$ phpunit --group unit txt/articles/

..

Time: 0 seconds, Memory: 5.25Mb

OK (2 tests, 0 assertions)

Runs the union of two suites:

[17:41:09][giorgio@Desmond:~]$ phpunit --group unit,functional txt/articles/

...

Time: 0 seconds, Memory: 5.25Mb

OK (3 tests, 0 assertions)

Runs the difference (in the set theory sense) of two suites.

[17:37:25][giorgio@Desmond:~]$ phpunit --group unit --exclude-group application txt/articles/
.

Time: 0 seconds, Memory: 5.25Mb

OK (1 test, 0 assertions)

Take a look at an Indigo.Design sample application to learn more about how apps are created with design to code software.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}