There are different approaches to testing a web application. Functional testing is a top down approach which models the way users negotiate the system through its interface; unit testing, also known as regression testing, is a bottom up strategy in which each class is thoroughly tested individually.
Unit tests go through each method and ensure it works, and fails, as expected. They should be written at least as one goes along - there are programmers who think they should actually be written before the system is put together. Which sound good, but it is often not realistic.
Unit testing with PEAR’s PHPUnit2.
Unit testing is an approach that comes from the Java world, and was implemented with the JUnit package. This was ported to the PHP world with the PHPUnit PEAR package, which is no longer maintained. However, there’s a PHPUnit2 package, which is PHP 5 compatible and works quite well, but needs to be installed. This is normally done by simply typing the following on the server’s shell:
pear install PHPUnit2
The previous step will create a PHPUnit2 folder in your core packages location, which is usually /usr/local/lib/php. It could be different, though, and there is a shell command to find out exactly where it is: pear conifg-get php_dir
The next step consists in creating a folder called tests or unittest where we’ll put the scripts and test classes - there will be one test class for each one of your application classes. Tests are organized in tests units - each unit corresponds to a class or related group of classes. The PHPUnit2 framework allows test units to be run singly or in groups.
All test scripts follow the same template:
define( 'PHPUnit2_MAIN_METHOD', 'AppTests::main' );
require_once( 'PHPUnit2/Framework/TestSuite.php' );
require_once( 'PHPUnit2/TestUI/TestRunner.php' );
//include the test classes here:
//require_once( 'tests/class2BtestedTest.php' );
//etc ...
class AppTests
{
public static function main()
{
$ts = new PHPUnit2_Framework_TestSuite( 'Name of suite' );
$ts->addTestSuite( 'Class2btestedTest' );
//etc
PHPUnit2_TextUI_TestRunner::run( $ts );
}
}
AppTests::main();
The line:
define( 'PHPUnit2_MAIN_METHOD', 'AppTests::main' );
defines a constant which prevents the TestRunner::main() method from running immediately when the TestRunner class is included - it will run AppTests::main instead, but only when called explicitly.
Each class to be included in the test suite is added both in the require_once section and within the main method of the AppTests class. This is followed by TestRunner’s run method, which will test each method of each class. The last line of the script initiates the test.
The script can be saved to a file in the tests folder, and run from the command line or a phing task.
Creating test classes
As we go along and create new classes for the project, we should create a corresponding test class:
Class2Btested.class.php:
class Class2Btested
{
function __construct() {}
//...
}
tests/Class2BtestedTest.class.php:
require_once( 'Class2Btested.php' );
require_once( 'PHPUnit2/Framework/TestCase.php' );
class Class2BtestedTest extends PHPUnit2_Framework_TestCase
{
private $instance;
function setUp()
{
$this->instance = new Class2Btested();
}
function tearDown(){}
//...
}
The setUp method will be called first when running tests, and its purpose is to instantiate the class to be tested, as well as running any other initiation code required. The tearDown method will be called last, and will perform clean up if needed. All other methods, the ones which actually perform the tests, will be run in between, as long as their name starts with ‘test’ and require no arguments.
Class2Btested.class.php:
class Class2Btested
{
function __construct() {}
//...
function methodA( $arg1 ) {}
function methodB( $arg2 ) {}
}
tests/Class2BtestedTest.class.php:
require_once( 'Class2Btested.php' );
require_once( 'PHPUnit2/Framework/TestCase.php' );
class Class2BtestedTest extends PHPUnit2_Framework_TestCase
{
private $instance;
function setUp()
{
$this->instance = new Class2Btested();
}
function testMethodB()
{
$expected = [ 1, 3 ];
$this->instance->methodA( 'john=1', 'jill=3' );
$actual = $this->instance->methodB( 'john', 'jill' );
$this->assetEquals( $expected, $actual );
}
function testMethodB_noMethodA()
{
try
{
$this->instance->methodB( 'john', 'jill' );
}
catch( Exception $e ) { return; }
$this->fail( 'expecting exception as methodA had not been yet' );
}
function tearDown(){}
//...
}
In this contrived example, a class has two methods, methodA and methodB. If methodA hasn’t been run before methodB, methodB should throw an exception. The example shows two tests run on methodB. In the first, a generic one, we run methodA with some dummy data, and test methodB with one of TestCase assert methods to see whether it reacts as expected. assertEquals is one of a number of TestCase methods which can be used to test the result of a call - other methods include assertFalse, assertType, assertNotNull, and so on. In the second test we test whether methodB throws an exception when methodA wasn’t called first. If it does, the method returns and moves on to the next test; otherwise it will fail the test.
These two examples are just a sample of the type of tests that can be run for each of a class’s methods. It makes sense to write these tests as the same time the methods are written, and run them often to catch any unexpected error that creeps in.

