It's a new way of looking at an old problem.
Editor's note: This article is an excerpt from the new book Advanced Guide to PHP on IBM i.
Have you ever run into a situation where you built something or made a change somewhere only to have it break something else? If your answer is no, wait until you graduate and get a job. It is bound to happen, and sometimes it will happen with catastrophic effects.
Part of the problem is that we do not often test our applications very well. Yes, we press refresh and maybe use a debugger, but do we have a system for testing?
But beyond using a testing system, we must go a little deeper. To best use our testing system, we have to build a testable application. A testable application is generally more complicated than the typical PHP application. However, a testable application pays for itself exponentially because it lets us repeatedly test all parts of our application to verify that they are working properly from start to finish.
Test-driven development is the practice of building out an application based on tests, rather than building it in a fluid manner. The way we naturally build applications is that we have a problem, we figure out how to solve it, we build the application, and then we test it. Test-driven development flips that process around. Instead of initially building out the application, we build the tests first and then write our code to pass the tests. It sounds a little counterintuitive, because all through our schooling years, the last thing we did before going to the graduation party was be tested. However, building our tests at the outset provides a lot of benefits.
First, it forces us to think of how we intend to use the functionality that we are creating. Second, if we are building our tests before building out the application, we are more likely to consider the required range of inputs, making us more likely to test edge conditions. This testing will lead to more stable software. Third, it makes us more productive. Unit testing has been shown both in studies and in real life to make developers more productive overall, and it is one of the few practices that have virtually no downside.
Some might claim that because you must now write both test and actual program code, you are becoming less productive. Although this might seem true at first glance, in practice it is often not the case. When writing unit tests, you are testing individual pieces of functionality. With traditional debugging, you are trying to coax the browser into performing a certain action, whether or not it wants to. And though you might be writing more code, you are writing code that is intended to verify that the function is doing what it is supposed to be doing.
Unit testing can be a complex subject to cover, but even a basic understanding of it can pay for itself. I will explain only the basics of performing unit testing here. For a more in-depth treatment of the subjects, complete books are available that provide a deeper guide into how to do unit testing.
PHPUnit
By far, the most popular PHP unit testing software is PHPUnit. It is maintained by Sebastian Bergman, and you can it download at
www.phpunit.de. PHPUnit is also included in your copy of Zend Studio.
Generally, tests are created in a separate directory from the application to prevent them from being deployed along with the application. You can, of course, put the test files alongside your class structure and remove any unwanted tests as part of your deployment process. But it is usually easier to disallow a single directory than to hope that your pattern-matching packaging process excludes all your test files.
Your tests’ structure should generally follow the structure of the items you are testing. So say you have a class called My\Class. The files related to it should be as follows:
/lib
/My
/Address.php
/tests
/My
/AddressTest.php
One practice you can use in some scenarios is to build out your class structure first and stub out the methods that you will be using. Consider this class:
namespace My;
class Address
{
public function setState($state)
{
}
public function getState()
{
}
}
This example defines the method that the class will use but does not add any functionality to it. So you can open the PHPUnit test case window in Zend Studio and have Studio create the test class for you (Figure 7.1).
Figure 7.1: Creating a test class in the PHPUnit test case window
Truth be told, Zend Studio creates a relatively ugly file. So let’s clean that file up a little:
use My\Address;
class AddressTest extends PHPUnit_Framework_TestCase {
/**
*
* @var Address
*/
private $Address;
protected function setUp() {
parent::setUp ();
$this->Address = new Address(/* parameters */);
}
public function testSetState() {
// TODO Auto-generated AddressTest->testSetState()
$this->markTestIncomplete ( "setState test not implemented" );
$this->Address->setState(/* parameters */);
}
}
What is missing in this code? It is require_once calls to the individual class you are testing along with the unit testing code. When I create a new unit test, I remove the require_once calls. I also delete a lot of boilerplate comments and other things. But to load the classes, you must first install an autoloader.
To set this up, you define a bootstrap file. Often, the bootstrap will be a PHP file in the root of the test directory. The PHP file will be called before the test run to set up the environment. One thing you do in that file is bootstrap the tests with an autoloader:
set_include_path(
get_include_path()
. PATH_SEPARATOR . realpath( __DIR__ . '/../lib')
. PATH_SEPARATOR . realpath( __DIR__)
);
spl_autoload_register(function($class) {
if (strpos($class, '\\') !== false) {
$class = str_replace('\\', '/', $class);
} else if (strpos($class, '_') !== false) {
$class = str_replace('_', '/', $class);
}
$filename = $class . '.php';
include $filename;
});
This bootstrap file can contain any functionality required to prepare the system for a test. Here, you needed only an autoloader and to set the include path. But in some cases, you might have to set a database connection or change a web service URL to a debug service.
You still have a problem, though. PHPUnit does not know where the bootstrap is. Luckily, you can configure PHPUnit with this information (and much more). PHPUnit will look for a file called phpunit.xml, which can contain various configuration options to modify how it works. Following is an example phpunit.xml file from the PHPUnit manual:
<phpunit backupGlobals="true"
backupStaticAttributes="false"
<!--bootstrap="/path/to/bootstrap.php"-->
cacheTokens="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
mapTestClassNameToCoveredClassName="false"
printerClass="PHPUnit_TextUI_ResultPrinter"
<!--printerFile="/path/to/ResultPrinter.php"-->
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
<!--testSuiteLoaderFile="/path/to/StandardTestSuiteLoader.php"-->
strict="false"
verbose="false">
<!-- ... -->
</phpunit>
For your purposes, you need only one attribute set:
<phpunit
bootstrap="bootstrap.php">
</phpunit>
With that, you are ready to go.
Editor's note: To learn more about using PHPUnit in Zend Studio for test-driven development, see Chapter 7 of Advanced Guide to PHP on IBM i.
LATEST COMMENTS
MC Press Online