Now that Zend_Test has shipped, developers are of course asking, "How do I setup my test suite?" Fortunately, after some discussion with my colleagues and a little experimenting on my one, I can answer that now.
PHPUnit offers a variety of methods for setting up test suites, some trivial and some complex. The Zend Framework test suite, for instance, goes for a more complex route, adding component-level suites that require a fair amount of initial setup, but which allow us fairly fine-grained control.
However, testing and test automation should be easy and the complex approach is overkill for most of our applications. Fortunately, PHPUnit offers some other methods that make doing so relatively simple. The easiest method is to use an XML configuration file.
As an example, consider the following:
<phpunit> <testsuite name="My Test Suite"> <directory>./</directory> </testsuite> <filter> <whitelist> <directory suffix=".php">../library/</directory> <directory suffix=".php">../application/</directory> <exclude> <directory suffix=".phtml">../application/</directory> </exclude> </whitelist> </filter> <logging> <log type="coverage-html" target="./log/report" charset="UTF-8" yui="true" highlight="true" lowUpperBound="50" highLowerBound="80"/> <log type="testdox-html" target="./log/testdox.html" /> </logging> </phpunit>
First thing to note, relative paths are relative to the configuration file.
This allows you to run your tests from anywhere in your tests tree. Second,
directory directive to the
testsuite directive scans for all
files ending in
Test.php in that directory, meaning you don't have to keep a
list of your test cases manually. It's a great way to automate the suite.
Third, the filter directive allows us to determine what classes to include
and/or exclude from coverage reports. Finally, the
logging directive lets us
specify what kinds of logs to create and where.
Drop the above into
tests/phpunit.xml in your application, and you can start
writing test cases and running the suite immediately, using the following
$ phpunit --configuration phpunit.xml
I like to group my test cases by type. I have controllers, models, and often library code, and need to keep the tests organized both on the filesystem as well as for running the actual tests. There are two things I do to facilitate this.
First, I create directories. For instance, I have the following hierarchy in my test suite:
tests/ phpunit.xml TestHelper.php controllers/ IndexControllerTest.php (contains IndexControllerTest) ErrorControllerTest.php (contains ErrorControllerTest) ... models/ PasteTest.php (contains PasteTest) DbTable/ PasteTest.php (contains DbTable_PasteTest) ... My/ Form/ Element/ SimpleTextareaTest.php
controllers/ contains my controllers,
models/ contains my models. If I were
developing a modular application, I'd have something like
instead. Library code is given the same hierarchy as is found in my
Second, I use docblock annotations to group my tests. I add the following to my class-level docblock in my controller test cases:
/** * @group Controllers */
Models get the annotation
@group Models, etc. This allows me to run
individual sets of tests on demand:
$ phpunit --configuration phpunit.xml --group=Controllers
You can specify multiple
@group annotations, which means you can separate
tests into modules, issue report identifiers, etc; additionally, you can add
the annotations to individual test methods themselves to have really
fine-grained test running capabilities.
Astute readers will have noticed the
TestHelper.php file in that directory
listing earlier, and will be wondering what that's all about.
A test suite needs some environmental information, just like your application
does. It may need a default database adapter, altered
autoloading set up, and more. Here's what my
TestHelper.php looks like:
<?php /* * Start output buffering */ ob_start(); /* * Set error reporting to the level to which code must comply. */ error_reporting( E_ALL | E_STRICT ); /* * Set default timezone */ date_default_timezone_set('GMT'); /* * Testing environment */ define('APPLICATION_ENV', 'testing'); /* * Determine the root, library, tests, and models directories */ $root = realpath(dirname(__FILE__) . '/../'); $library = $root . '/library'; $tests = $root . '/tests'; $models = $root . '/application/models'; $controllers = $root . '/application/controllers'; /* * Prepend the library/, tests/, and models/ directories to the * include_path. This allows the tests to run out of the box. */ $path = array( $models, $library, $tests, get_include_path() ); set_include_path(implode(PATH_SEPARATOR, $path)); /** * Register autoloader */ require_once 'Zend/Loader.php'; Zend_Loader::registerAutoload(); /** * Store application root in registry */ Zend_Registry::set('testRoot', $root); Zend_Registry::set('testBootstrap', $root . '/application/bootstrap.php'); /* * Unset global variables that are no longer needed. */ unset($root, $library, $models, $controllers, $tests, $path);
The above ensures that my
APPLICATION_ENV constant is set appropriately, that error reporting is appropriate for tests (i.e., I want to see all errors), and that autoloading is enabled. Additionally, I place a couple items in my registry — the bootstrap and test root directory.
In each test case file, I then do a
require_once on this file. In future
versions of PHPUnit, you'll be able to specify a bootstrap file in your
configuration XML that gets pulled in for each test case, and you'll be able to
even further automate your testing environment setup.
Hopefully this will get you started with your application testing; what are you waiting for?