Brought to you by molecularsciences.org.
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 License.
This publication may not be redistributed without this notice.

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 it 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.

Why unit test
The goal of unit testing is to isolate pieces of a program/application/software and then show that each piece is functioning correctly. Unit tests find problems early in the development cycle thus reducing errors and debugging time later on. Unit tests allow programmers to retest their code in future and make regression testing easier. Since unit tests include test cases, they serve as a form of documentation. It is important to note that unit test do NOT replace higher level functional or system testing.

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 testing is that it helps me write better code and make it easier to find and fix errors. Testing is boring but not frustrating.

Programming languages supporting unit testing
Java and Python 2.1 and above come with a unit testing framework. Java provides JUnit and Python provides unittest. Most languages do not come with pre-package unit testing frameworks but free open source unit testing frameworks are readily available online for all major languages. For example, SimpleTest and PHPUnit are available for PHP. In addition, several open source software such as Drupal, Moodle, and Elgg integrate unit testing frameworks in their core to enable fast, easy, and efficient unit testing.

Bird's eye view of unit testing

Key activities in software development include building, debugging, and testing. Testing is the process of verifying that a software fulfills it specified requirements. It is also a measure of software quality. Testing helps reduce the cost of software development and ownership.

The software requirements specification of a software must set benchmarks, list features, and these features should serve as a basis for testing the software. Ask yourself whether the software fulfills each and every requirement as it is described. When writing the requirements and specifications, it is important to ensure that the wording is clear and unambiguous, each requirement is testable, and most importantly the requirements must fulfill the needs of the customer.

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 whether it is still an array
if (!is_array($var2)) { print 'error: $var2 is not an array'; }
if (!empty($var2)) { print 'error: $var2 is not empty'; }
$var2[] = 'adam';

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

$var2 = array();
// ... some code    
// it is a good idea to test whether is empty and whether it is still an array   
assertTrue(is_array($var2), '$var2 is not an array');
assertTrue(empty($var2), '$var2 is not empty');
$var2[] = 'adam';

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

Once again, the system complains 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 provided on this site. SimpleTest has been integrated into Elgg 1.7 and onwards, Moodle, and Drupal.

Installing SimpleTest
Simply download the latest stable release. Unzip it into your working directory
$ tar xzvf simpletest_1.0.1.tar.gz

SimpleTest Example

require_once('simpletest/autorun.php');

class MyExample extends UnitTestCase {   
    function myex() {
        $var2 = array();        
        $this->assertTrue(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.

Unit Testing in Elgg

Do NOT do this on production environments!!

Elgg 1.7 and above includes PHP SimpleTest framework. Here you will see how to unit test in Elgg.

First of all, log into your dev environment with admin access. Go to:
Admin > Site Administration and change "Turn off debug mode (recommended)" to "Display errors and warnings".
Then we create a new plugin. This example would have the following structure:

|- mod 
|     |- utest 
|     |     |- manifest.xml
|     |     |- start.php
|     |     |- tests 
|     |     |     |- utest.php

manifest.xml

<?xml version="1.0" encoding="UTF-8"?>
<plugin_manifest>
<field key="author" value="Nazim Rahman" />
<field key="version" value="1.0" />
<field key="description" value="unit testing example" />
<field key="website" value="" />
<field key="copyright" value="" />
<field key="licence" value="GNU Public License version 2" />
<field key="elgg_version" value="2011071212" />
</plugin_manifest>

start.php

function utest_init() {
    register_plugin_hook('unit_test', 'system', 'utest_unit_tests');
}
function utest_unit_tests($hook, $type, $value, $params) {
    global $CONFIG;
    $value[] = $CONFIG->path . 'mod/utest/tests/utest.php';
    return $value;
}
function utest_function1() {
    $var = 1;
    if ($var == 1) { return true; } else { return false; }
}
register_elgg_event_handler('init','system','utest_init');

tests/utest.php

class UtestUnitTest extends ElggCoreUnitTest {
    public function testFunction1() {
        $value = utest_function1();
        $this->assertTrue($value);
    }
}

To run this unit test, you need of course enable the plugin utest. Then go to:

Admin > System Diagnostics > Execute All

Note that start.php itself does not contain any debugging code or unit testing code. The unit testing code is in separate file inside tests directory. This directory is not pushed to production.

The beauty of this solution is that you can test all your plugins in dev with one click even years after writing them.

SimpleTest list of assert functions

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