Automatic Testing with PHP

17 Mar 2011 by Martin Vium

Want to commit Friday night? Tired of the same bug appearing again, when already fixed it once? Constantly writing small test.php scripts to test behavior?

Why not organize the whole thing, so you only have to press a button to ensure everything works as intended, or better yet, do it automatically whenever you commit something to your repository?

PHPUnit allows for all that and more, it can be intimidating at first, but if keep it simple, it’s not very hard and not very time consuming.

You need to install PHPUnit on your local computer and configure it with your IDE of choice (move along if your using notepad).

Configuration

Add a folder to your checkout/clone called “tests” containing the following:

library/
tests/
  My/
    Tests/
      Here.php
  bootstrap.php
  phpunit.xml

In this case library is the directory containing the code you want to test, but pretty much any structure will work. The bootstrap file contains the minimum to get your code (and your tests) running, usually something like an include path and autoloader:

<?php
$rootPath = realpath(dirname(dirname(__FILE__)));
$libraryPath = $rootPath . '/library';
$testsPath = $rootPath . '/tests';

$paths = array(
  $libraryPath,
  $testsPath,
  get_include_path()
);

set_include_path(implode(PATH_SEPARATOR, $paths));

spl_autoload_register(function($class) {
  return spl_autoload(str_replace('\\', '/', $class));
});
?>

The configuration file is called phpunit.xml, which PHPUnit will look for by default. This configures how your unit tests should be run, and among other things defines the name of your bootstrap file:

<?xml version="1.0"?>
<phpunit bootstrap="./bootstrap.php">
    <testsuite name="My Test Suite">
        <directory>./</directory>
    </testsuite>
    <groups>
        <exclude>
            <group>disable</group>
        </exclude>
    </groups>
    <filter>
        <whitelist>
            <directory suffix=".php">../library</directory>
        </whitelist>
    </filter>
</phpunit>

Now your ready...

Wait, how do you test your code? Well that's easy! In the My/ directory we add a file, I like to keep the exact same structure as in my library for unit-tests. But basically all the rules of normal code applies to your tests as well. E.g. refactor large classes into smaller ones, use inheritance where it makes sense etc.

Here is an example called tests/My/SimpleXMLTest.php (not our class, but we will borrow it for now). And while we are at it, we might as well just parse our PHPUnit configuration file (because it's already there!).

<?php
namespace My;

class SimpleXMLTest extends \PHPUnit_Framework_TestCase
{
    public function testMagicGet_BasicStringExample_ReturnsDirectoryValue()
    {
        $path = dirname(dirname(__FILE__)) . '/phpunit.xml';
        $xml = simplexml_load_file($path);
        $actual = (string)$xml->testsuite->directory;
        $this->assertEquals('./', $actual);
    }
}
?>

There is a couple of things to note here:

I name the method according to some advice I found on the web:

testMethod_Scenario_Assertions()

There are many assertX() methods, but they function pretty much the same. An expected value and the actual return value. You should always try to expect a static value (first argument).

You should absolutely minimize the logic of your test (no if's, switches or loop!) helper functions are great though. I keep my tests in the same namespace as my actual code (but not the same directory). Your IDE should ignore test classes for auto-completion. Running the Tests

Will depend on what IDE you are using. In Netbeans Shift+F6 will run the selected test. Ctrl+F6 will run the test matching the selected implementation class.

Alternatively you can run the test suite manually:

$ phpunit <path-to-tests>