php-unit-test

What is Unit Testing

Most developers agree that testing is important but few test their code. Even fever do it methodically. Many developers do not realize that there is a difference between my code is working and my code is correct. My code is working means that I clicked around and I am getting the functionality I desire. My code is correct means that I have verified that my code will always do what I expect it to do. For example,

// $a = 1, $b = '1'
print $a + $b;

In this one line PHP code, the develop wishes to add two integers. What he doesn’t realize is that $b is a string. The line outputs the correct result but the developer is unaware of the implicit casting. Under rare cases, $a would also be cast to string or perhaps it was a string, which usually casted to int but in this particular case, it did not cast to int. The result is that this one line code is now adding two strings. The output is:

Parse error: syntax error, unexpected T_LNUMBER in x.php on line 2

If the developer had verified that the code is correct, this problem would not occur. Such problems are very common in PHP code today. Unit testing can help eliminate most of these errors and unit test can be used to verify that the code is correct.

Most developers test and debug by either clicking around or inserting debug statements to find the problem. These are desperate measures taken by someone who doesn’t know better or is unable to debug in an orderly fashion due to architectural constraints. Debugging in orderly fashion means unit testing. The architecture of a software can make is difficult to unit test. For example, it is difficult to unit test Moodle, Drupal and Elgg without editing the core. So all three have added support for unit testing in their core, making unit testing very easy and now developers to not have to edit the core to unit test.

There are two important concepts in unit testing:

  1. Think about what can go wrong with the code and write code to test for it
  2. Test at granular levels and fix problems early so that you have fewer problems later

A unit is the smallest testable piece of source code. A unit could be an object, function, or even a loop. For example, a unit test could be used to test whether a loop terminates prematurely or whether a function which is supposed to return a string is returning an integer. Unit tests are generally run every time a code has been changed anywhere. This way if a change inadvertently changed something else in the code, it would be reported right away and the developer would be able to fix it immediately rather than having to do painstaking debugging later when the inadvertent change becomes a visible bug.

The primary arguments against unit testing is that it takes too long (so much more code to write especially for newbies), relatively steep conceptual learning curve, assumption that nothing could go wrong with this piece of code, and most of us find testing to be boring and waste of our valuable time.

My experience with unit test shows that it helps me write better code and make it easier to find and fix errors.

Bird’s eye view of unit testing

There are many different forms of testing but the three important ones which would be discussed here are unit testing, integration testing, and regression testing. The purpose is only to provide a comparative understanding of the three.

Unit Testing
Unit testing involves isolating smallest testable pieces of code and then testing them for correctness. It only verifies that the small piece of code is doing what it is supposed to do.

Integration Testing
Integration testing is the logical next step after unit testing. It tests whether two or more units do what they are supposed to do when used together. For example, we have verified that the wheel is working properly with unit testing and we have verified that the steering wheel is working correctly. Now we need to verify whether wheel responds correctly to the changes made to the steering wheel.

Regression Testing
Regression tests should be run whenever a software’s implementation is modified. It is a series of tests designed to see whether something has broken as a result of the change in implementation. Unit tests greatly simplify regression test since they could be re-run at any time.

Debugging with Inserted Code

Suppose you are debugging and you wish to verify whether a variable in the following code is empty and an array before using it:

$var2 = array();
// some code here followed by
$var2[] = 'adam';

Several possible problems could arise for between two the two lines of code by the code which would be in between. We know that on the last line, we expect $var2 to be an empty array. One way to verify would be to insert the following code which would only complain when $var2 is not what we expected it to be.

$var2 = array();
// ... some code
// testing whether variable is empty and array
if (!is_array($var2)) {
   print 'error: $var2 is not an array';
}
$var2[] = 'adam';

A more elegant way to do the same would be to write a function for this as follows:

$var2 = array();
// ... some code
// testing whether variable is empty and array
assertTrue(is_array($var2),'$var2 is not an array');
assertTrue(empty($var2),'$var2 is not an array');
$var2[] = 'adam';

function assertTrue($condition, $msg) {
   if (!$condition) {
      print "Assertion failed: {$msg}";
   }
}

The system will complain only when something goes wrong.

Naturally, the next thing which would cross your mind is that there should be a library or framework for this. In fact, there are many frameworks but the two most commonly used ones are PHPUnit and SimpleTest.

SimpleTest

SimpleTest is a PHP unit testing and web testing framework. The complete API documentation is available at http://simpletest.org/api/. If you are using Eclipse, a SimpleTest plugin is also available. SimpleTest has been integrated into Elgg 1.7, Moodle, and Drupal.

Installing SimpleTest
Simply tun the following two command to install SimpleTest. You need GIT and Composer installed first.

git clone https://github.com/simpletest/simpletest/releases/latest
php composer.phar require --prefer-dist simpletest/simpletest "^1.1"

SimpleTest Example

require_once('simpletest/autorun.php');

class MyExample extends UnitTestCase {
   function myex() {
      $var2 = array();
      $this->assetTrue(is_array($var2));
   }
}

output

OK?
Test cases run: 0/1, Passes: 0, Failures: 0, Exceptions: 0

This example also involves inserting testing code into the original code. In reality, unit tests are written in separate files and these files are not ported into production environments.

SimpleTest list of assert functions

Following is a table that you can use for quick reference for simpletest functions you can use.

FunctionDescription
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
assertNotA($x, $t) Fail if $x is of the class or type $t
assertEqual($x, $y) Fail if $x == $y is false
assertNotEqual($x, $y) Fail if $x == $y is true
assertWithinMargin($x, $y, $m) Fail if abs($x – $y) < $m is false
assertOutsideMargin($x, $y, $m) Fail if abs($x – $y) < $m is true
assertIdentical($x, $y) Fail if $x == $y is false or a type mismatch
assertNotIdentical($x, $y) Fail if $x == $y is true and types match
assertReference($x, $y) Fail unless $x and $y are the same variable
assertClone($x, $y) Fail unless $x and $y are identical copies
assertPattern($p, $x) Fail unless the regex $p matches $x
assertNoPattern($p, $x) Fail if the regex $p matches $x
expectError($x)Swallows any upcoming matching error
assert($e)Fail on failed expectation object $e

By master