Web applications run 24x7, so the question of whether my program is still running bothers me at night. Unit testing has helped me build enough confidence in my code that I can sleep well.
Unit testing is a framework for writing test cases for your code and running these tests automatically. Test-driven development is a unit testing approach based on the idea that you should first write tests and verify that these tests can find errors, and only then start writing the code that needs to pass these tests. When all tests pass, the feature we developed is complete. The value of these unit tests is that we can run them at any time - before checking in the code, after a major change, or after deployment to a running system.
PHP unit testing
For PHP, the unit testing framework is PHPUnit2. This system can be installed as a PEAR module using the PEAR command line: % pear install PHPUnit2.
After installing this framework, you can write unit tests by creating test classes derived from PHPUnit2_Framework_TestCase.
Module Unit Testing
I have found that the best place to start unit testing is in the business logic module of the application. I'm using a simple example: this is a function that sums two numbers. To start testing, we first write the test case as shown below.
Listing 1.TestAdd.php
<p><?php <br>require_once 'Add.php';<br>require_once 'PHPUnit2/Framework/TestCase.php';</p><p>class TestAdd extends PHPUnit2_Framework_TestCase<br>{<br>function test1() { $this->assertTrue( add( 1, 2 ) == 3 ); }<br>function test2() { $this->assertTrue( add( 1, 1 ) == 2 ); }<br>}<br>?></p> Copy after login |
This TestAdd class has two methods, both of which use the test prefix. Each method defines a test, which can be as simple as Listing 1 or very complex. In this case, we simply assert that 1 plus 2 equals 3 in the first test, and 1 plus 1 equals 2 in the second test.
PHPUnit2 system defines the assertTrue() method, which is used to test whether the condition value contained in the parameter is true. We then wrote the Add.php module, which initially produced incorrect results.
Listing 2.Add.php
<p><?php <br>function add( $a, $b ) { return 0; }<br>?></p> Copy after login |
Now when running the unit tests, both tests will fail.
Listing 3. Test failure
<p>% phpunit TestAdd.php<br>PHPUnit 2.2.1 by Sebastian Bergmann.</p><p>FF</p><p>Time: 0.0031270980834961<br>There were 2 failures:<br>1) test1(TestAdd)<br>2) test2(TestAdd)</p><p>FAILURES!!!<br>Tests run: 2, Failures: 2, Errors: 0, Incomplete Tests: 0.</p> Copy after login |
Now I know both tests work. Therefore, the add() function can be modified to actually do the actual thing.
<p><?php <br>function add( $a, $b ) { return $a+$b; }<br>?></p> Copy after login |
Both tests now pass.
Listing 4. Test passed
<p>% phpunit TestAdd.php<br>PHPUnit 2.2.1 by Sebastian Bergmann.<br>..<br>Time: 0.0023679733276367<br>OK (2 tests)<br>%</p> Copy after login |
Although this example of test-driven development is very simple, we can appreciate it Thought. We first created the test case and had enough code to make the test run, but the result was wrong. Then we verify that the test indeed fails, and then implement the actual code to make the test pass.
I find that as I implement code I keep adding code until I have a complete test that covers all code paths. At the end of this article, you'll find some advice on what tests to write and how to write them.
1