Unit Testing and Joomla!

As we move forward with Joomla! Development one element that is going to be playing a larger and larger role is unit testing. My hope with this short series is to provide some basic background on unit testing, to demonstrate how essential unit tests are to stable software and to give practical instruction and examples that illustrate how to write unit tests.

As co-maintainer of the Joomla! Bug Squad, there is a question that repeatedly pops up: 'What is this change going to break?'. There are certain elements of the code base that can be rather fragile and this makes it very difficult to know what things are safe to touch and what things aren't. The component routers are a good example of this. We are very very hesitant to touch this because many people are very particular about their URLs, and past experience has shown that small changes can cause significant, unforeseen problems.

Good unit tests take a great deal of the anxiety out of such changes because you can be confident that the change you are introducing is not going to have nasty, unforeseen consequences.

What is Unit Testing?

In its purest form, unit tests are tests that are designed to ensure that small units of code behave as they are intended. Pure unit testing means that each method is tested in isolation from all other methods. That is to say, for example, that if we are writing a test for JApplication, and JApplication happens to use the JDatabase class, we want to try and design our tests such that a bug in the JDatabase class won't affect our test written for the JApplication class.

In practice, this can be difficult because sometimes class references are hardcoded. Static methods are very difficult in this regard because you can't redefine classes and all tests in PHPUnit are executed in the same process.

On the other end of the scale is system testing. System tests are tests that are written to test a system as a whole. In these tests, the goal isn't to isolate particular units of code. Rather, it is to ensure that all the parts work together in the way they were intended. For web applications, these tests are often developed using a product called Selenium which allows you to control a web browser in order to simulate a user actually using your web application.

Installing PHPUnit

We are slowing building up an extensive suite of unit tests for the Joomla! Framework. The tests are built on a unit testing framework called PHPUnit. In order to run the tests, you need to obtain PHPUnit. To get PHPUnit, head over to PHPUnit Installation and follow the instructions to install PHPUnit.

Running Tests

Unit tests are typically run from the command line. While this may be daunting for some, once you get the hang of things it is actually quite easy. Once you have PHPUnit installed and your path setup correctly, it is as simple as changing to the unit test directory and typing 'phpunit': cd /path/to/joomla/tests/unit phpunit After you run this command, you should see output that looks something like:

There are many situations where you may not want to run the whole test suite. See the Running Unit Tests page for details on how to be selective with the tests that you run.

Anatomy of a Test

Unit tests are merely small chunks of PHP code that:

Setup certain preconditions for the code to be tested (inputs).

Execute the code to be tested.

Tests assertions about the results (outputs).

We will see these basic elements in each of the example tests that are provided below.

Writing Our First Test

The easiest and most simple type of unit test to write is a test for code that is stateless. That is, a test for which the return value of the method depends solely on the inputs to the method. Most of the basic PHP functions are examples of stateless functions. The strtoupper function, for example, takes one parameter – a string – and returns a string value. You can easily create a set of inputs and corresponding outputs and write a test. Examples in the Joomla! Framework include much of the JArrayHelper class. Most of these methods are static methods and don't depend on anything except the data passed to the method. For this example, we will use the JArrayHelper::getColumn method. This method takes two parameters and returns an array. So, following the three steps outlined above, we break things down into three elements:

1. Setup Preconditions

In this case, there are no preconditions because our method does not depend on anything other than the parameters that are passed to it. Our only preconditions are the parameters that we pass to the method.

2. Execute the code to be tested

So, our method in question will take an array of associative arrays or objects and will return an array of the values of the specified index. So, to test our method, we first need to create an array of associative arrays:

We have now executed the method we want to test. We can now examine the results.

3. Tests assertions about the results (outputs)

Since this method is fairly simple, testing the results is also simple. What we need to do is figure out what we expect to get back from the method. In this case, we expect to get an array containing an array of the countries.

$this->assertThat($result_array,$this->equalTo(array('United States','Germany','Canada')),'We did not get the proper column data back');

Our final test looks like:

publicfunction testGetColumnTutorial(){$test_array=array(array('sport'=>'football','teams'=>'16','country'=>'United States'),array('sport'=>'badminton','teams'=>'12','country'=>'Germany'),array('sport'=>'basketball','teams'=>'20','country'=>'Canada'));$result_array= JArrayHelper::getColumn($test_array,'country');$this->assertThat($result_array,$this->equalTo(array('United States','Germany','Canada')),'We did not get the proper column data back');}

How a Unit Test should NOT be written

Unexperienced users often tap into pitfalls when writing unit test testcases. A good tutorial on how to avoid these pitfalls, sometimes also called anti-patterns, is summarized at [1].

Unit Test Tutorial 2

This article is continued here: Unit Test Tutorial 2. This includes looking into the derived 'PHPUnit_Framework_TestCase' testclass and the minimum requirements for a standalone testclass.