You want to test your Drupal code? We’ve learned how to do it, the hard way, through 3 years of experience with continuous integration at The Economist. And I can distill our current approach into a few short paragraphs:
1. Use Simpletests to unit test code.
Unit testing is vital, but it’s also difficult in Drupal, because a lot of time you’re actually writing glue code, or exporting Views, or theming. Code that inherently relies on the rest of the environment to be setup in order to function at all.
However, it is possible to write code in such a way that it’s easier to unit test. By abstracting core functionality into function libraries or classes, independent of any hooks, it’s still possible to give your code the once-over. For this, Simpletest is great. Check out the
DrupalUnitTestCase for this, in the
Simpletest module. The simulated test environment that Simpletest usually creates is not necessary for these unit tests.
2. Use Simpletests to test self-contained Drupal modules.
When I say “self-contained”, I mean modules that only rely on core Drupal in order to work, and encapsulate a very specific function. This is the classic form of Simpletest that is used on Drupal.org: install default Drupal in a sandbox created using table name prefixing, setup the dependencies, and run. We use many contrib modules that provide Simpletests, and we run these and fix them up where-ever possible. Any custom modules we develop that only rely on ‘standard’ Drupal core modules will also have Simpletests. These modules should be able to run with the bare minimum, using the Garland theme.
3. Use Selenium tests for functional acceptance testing.
In order to test glue code, you really need to drive the browser against a full copy of the site. You know the code I’m talking about, it’s all that stuff in
template.php that changes UI elements or hides default Drupal behaviour using hooks… or those pesky attached Views that really need to work properly, but keep disappearing? Yeah those.
Selenium is a great choice for this, although it’s a challenge setting up an automated runner that builds reliably, and of course decrypting the Selenium API and relationship to PHPUnit is non-trivial, we’ve solved these issues and I’ll be sharing them in upcoming posts.
What You Should Not Do, Lest Ye Be Cast Into Testing Hell
- If you write a Simpletest to test a glue code module, you’ll quickly find that you need to setUp() most of the other modules in your site in order to provide the glue code with the environment it expects. This is a one way street to Testing Hell. Any Simpletest that needs to setUp() more than 5-10 modules will be slow, unreliable, impossible to debug and will be broken all the time by seemingly unrelated changes even though the site still works perfectly well.
- Do not code a fancy trick to enable your site’s theme in your Simpletest. Simpletest was not designed to test code in themes, and the only thing Simpletest hates more than theme code, is fancy tricks. It will immediately begin prepping the trébuchet that shall be used to cast you into Testing Hell if you try pull a fancy trick on it.
- Do not attempt to use
memcached on the system that’s running the Simpletests. The simulated environment will then share it’s cache with the outer parent environment, as Simpletest expects the table prefixing to keep them separate, which of course won’t happen when using memcached.
- Don’t forget your curly braces around table names in your SQL queries, in all your code, otherwise Simpletest execution will query your outer parent tables instead of the simulated environment tables. Oh yes.
- If you realise that you need to setUp() a feature (Features module) in order to Simpletest your code, perhaps to use a content type that’s defined within it, stop. Don’t. Certain death.
That’s it really: three steps and five sacred commandments that lead to overall respectable test coverage and a generally sane dev team. All our tests run continuously, using Hudson, the Rackspace cloud and a lot of clever scripts. For each commit to trunk, we get an email for failures and a happy Chuck Norris for each successful build.