Testing PHP programs with PHPUnit
The importance of testing your code cannot be overemphasized. It gives you peace of mind whenever you make any changes, including making an overhaul of the organization of your code. In this article, we describe how to use PHPUnit to test your PHP programs.
Code preparation
Before delving into the details of using PHPUnit, it is important to follow a few simple rules to make your PHP programs easy to test.
First, even though most of your PHP programs run under a Web server like apache's context, it is important that you decouple it from the Web server as much as possible. For example, reading and writing the global variables such as $_REQUEST, $_GET, $_POST, $_SERVER that are meaningful only in the Web context should be placed in a central location. All your functions should avoid manipulating these variables directly. Instead, you should copy them over to a generic data hash $data and then operate on that hash. In this way, you can just populate the $data hash and pass it to your functions whenever you need to read from or write to it.
Second, please read, understand and follow the Model-View-Controller (MVC) model if you can. The MVC model is not just useful for developing desktop GUI applications but also useful for Web applications. Separation of your business logic such as data manipulation from presentation such as whether you use a table or a graph to show the data is the key point. Following this principle your code will be easier to test.
Installation
Installing PHPUnit is very simple. You can do it through PEAR - PHP Extension and Application Repository which should have been installed if you follow the article Create a local LAMP development environment.
You should first need to run the following commands to add additional channels through which you can find the PHPUnit package:
pear channel-discover pear.phpunit.de
pear channel-discover pear.symfony-project.com
Next you can run either pear install phpunit/PHPUnit
or pear install --alldeps phpunit/PHPUnit.
The second command is simpler because it also installs all the dependent
packages without prompting you for confirmation.
Then you can run pear list -a to verify that PHPUnit is
indeed installed.
Writing test cases
Writing test cases with PHPUnit is very simple.
You just need to include the
PHPUnit/Framework.php file, declare a class that extends
PHPUnit_Framework_TestCase and then have all your testing
functions starting with the name "test" and use $this->assertTrue
to check whether the condition is met.
The following shows
a complete but very simple example using PHPUnit.
require_once 'PHPUnit/Framework.php';
class SimpleTest extends PHPUnit_Framework_TestCase {
public function testSimple() {
$this->assertTrue(1+1 == 2);
$this->assertEquals(1+2, 3);
}
}
?>
Then you can run it with
phpunit phpunit_simple.php
and get the following output:
PHPUnit 3.4.12 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 3.50Mb
OK (1 test, 2 assertions)
Probably that's all you need to know in most cases. If you need advanced features, you can check the PHPUnit manual.
Resolution to some common PHPUnit usage issues
Following we describe some issues that you may see when working with PHPUnit and how to address them.
Testing with PHP resources
When you test PHP functions that reuse the same resources, you may see some weird errors that say the given parameter is not a resource. Following is a simple script that reproduces the resource issue:
<?php
require_once 'PHPUnit/Framework.php';
global $ch;
$ch = curl_init();
function do_curl()
{
global $ch;
curl_setopt($ch, CURLOPT_URL, "http://www.yuonlamp.com/");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$ret = curl_exec($ch);
return strlen($ret)>0;
}
class CurlTest extends PHPUnit_Framework_TestCase {
public function testCurl() {
$this->assertTrue(do_curl());
}
public function testCurl2() {
$this->assertTrue(do_curl());
}
}
?>
When you run
phpunit phpunit_curl.php
it outputs errors like the following:
PHPUnit 3.4.12 by Sebastian Bergmann.
.E
Time: 1 second, Memory: 3.50Mb
There was 1 error:
1) CurlTest::testCurl2
curl_setopt() expects parameter 1 to be resource, integer given
/tmp/phpunit_curl.php:10
/tmp/phpunit_curl.php:22
FAILURES!
Tests: 2, Assertions: 1, Errors: 1.
The reason is that PHPUnit's backup and restoration of global variables does not work for resources. You can ask PHPUnit not do to this by adding
protected $backupGlobals = FALSE;
to your testcase class that derives from
PHPUnit_Framework_TestCase.
You can download the modified PHPUnit test script that works when resources are used.
You can also check the Global State section in the PHPUnit manual for detailed discussions on this.