Unit Testing PHP

By Paulus, 29 December, 2007

A while back I was working on a site, and as I was writing the back end and making sure that it works properly I thought to myself "If .NET has nunit, and Java has junit, does PHP have phpunit, or something similar?" After some digging I found the answer. The answer I found was yes! In addition to finding unit frame works for PHP I also found some for other languages like ada, haskell and so on:

aunit - for Ada
hunit - for Haskell
ant-junit - Apache Ant's optional tasks depending on junit
dbunit - DBUnit is a JUnit extension targeted for database-driven pr
ojects.
tagunit - for testing custom JSP tags
xmlunit - for XML
and so on. If you want a complete list, on the command prompt, if you're using Gentoo, just type:

# emerge --search unit | less

and have fun!

For PHP there are a few different ones:

dev-php4/phpunit
dev-php5/phpunit
dev-php/simpletest

At the time of writing this, dev-php4/phpunit is masked, but the version of phpunit for PHP 5 is not. I chose simpletest because I didn't have to unmask any packages in order to install it.

# emerge simpletest

BAM! Done!

Here's the basic structure of a unit test file:

require_once("simpletest/unit_tester.php");
require_once("simpletest/reporter.php");

class TestClass extends UnitTestCase {

function setUp(){

// set up code here such as creating files or initiating connections.

}

function tearDown() {

// perform any cleanup here such as deleting files or closing connections.

function testFunction() {

// insert test code here

}

}

The setUp() and tearDown() functions are pretty self explanitory. The setup is where you palce all the code that you need in order to start testing. The tearDown() is where you clean up the mess that you've made. Within the other function is where the testing actually takes place. The class that is used in the example can have many functions. Each function is run. If all asserts pass, then the function passes. As soon as something fails then the function stops executing. The assert functions are apart of the UnitTestCase class. In order to access these functions you would use $this to call them. Some of the assert functions are:

assertTrue($x) Fail if $x is false
assertFalse($x) Fail if $x is true
assertNull($x) Fail if $x is set
assertNotNull($x) Fail if $x not set
assertIsA($x, $t) Fail if $x is not the class or type $t
assertEqual($x, $y) Fail if $x == $y is false
assertNotEqual($x, $y) Fail if $x == $y is true
assertIdentical($x, $y) Fail if $x === $y is false
assertNotIdentical($x, $y) Fail if $x === $y is true
assertReference($x, $y) Fail unless $x and $y are the same variable
assertCopy($x, $y) Fail if $x and $y are the same variable
assertWantedPattern($p, $x) Fail unless the regex $p matches $x
assertNoUnwantedPattern($p, $x) Fail if the regex $p matches $x
assertNoErrors() Fail if any PHP error occoured
assertError($x) Fail if no PHP error or incorrect message

A test case is not limited to only functions that are to be tested, by adding functions that don't start with the word test can we more thoroughly test our application.

require_once("simpletest/unit_tester.php");
require_once("simpletest/reporter.php");

class TestClass extends UnitTestCase {

function setUp(){

// set up code here such as creating files or initiating connections.

}

function tearDown() {

// perform any cleanup here such as deleting files or closing connections.

function testFunction() {

// insert test code here

}

function doSomething() {

// This function will not be tested when run.

}

Group Tests

A nice feature that we can use is grouping the test together. Take this Test case for example:

if(! defined('SIMPLE_TEST')) {
define('SIMPLE_TEST', 'simpletest/');
}
require_once(SIMPLE_TEST . 'unit_tester.php');
require_once(SIMPLE_TEST . 'reporter.php');
require_once('test_class.php');

$test=&new GroupTest('All Tests');
$test->addTestCase(new TestClass());
$test->run(new HtmlReporter());

?>

The test group isn't that much different from a TestCase file. We create an object of GroupTest and then we add the TestCase to the list of things we want to test with:

$this->addTestCase( new TestClass());

Unit Testing with PHP is pretty straight foreword. Testing cookies, sessions, and headers is a bit different and will be covered later.