Writing SimpleTests for a CiviCRM installation
On a recent project involving CiviCRM, I really needed to write tests for the workflow, which was very complex, and without tests, brittle.
The solution, partially, is Drupal's SimpleTest (which now uses it's own framework, instead of the original SimpleTest library). I say partially, because unfortunately, CiviCRM sits completely outside of the Drupal API, and doesn't play well with things such as hook_install(), which the Drupal testing framework uses to build a parallel testing database each time tests are run.
As such, the mostly complete solution was to use my custom workflow module's setUp() method to copy the existing CiviCRM database, and then run tests against that:
/** * Implementation of setUp(). */ function setUp() { parent::setUp('civicrm', 'os_custom', 'os_civicompany'); // Now, clone civicrm install global $db_prefix; $test_db = db_set_active('civicrm'); // Build list of tables // @todo - implement this for postgresql $result = db_query("SHOW TABLES"); while ($table = db_result($result)) { // Build list $this->tables[] = $table; } // Clone tables with prefix foreach ($this->tables as $table) { // Note, we intentionally DON'T use the bracketed table names here, // since the whole point is to create a set of tables that will work // with the testing $db_prefix. db_query("CREATE TABLE " . $db_prefix . $table . " AS SELECT * FROM " . $table); } db_set_active($test_db); }
This set of code first runs the parent setUp() method, which creates a new Drupal instance inside the existing database, but with all the tables prefixed with the $db_prefix. It then switches to the CiviCRM database, and copies each table into an identical table, but named with the testing prefix. On advantage, that is mostly a side-effect, is that instead of having a fresh install of a CiviCRM database, I get a testing copy that is configured exactly the same way as the application I am trying to test in the first place*.
In order to clean up after the test, a custom tearDown() method is also needed:
/** * Implementation of tearDown(). */ function tearDown() { // Cleanup CiviCRM database. global $db_prefix; if ($db_prefix && ($db_prefix != $this->db_prefix_original)) { // Only drop tables if $db_prefix is set, and not equal to the original $test_db = db_set_active('civicrm'); foreach ($this->tables as $table) { db_query("DROP TABLE " . $db_prefix . $table); } db_set_active($test_db); } parent::tearDown(); }
This simply goes and removes the prefixed copy of the CiviCRM database.
These two methods allow for my custom Drupal modules to test against the CiviCRM database in most cases. Where this method fails is for testing actual calls to the CiviCRM API. Since this API sits completely outside of Drupal, it isn't fooled by simply switching the global $db_prefix for the tests. It may be possible to trick CiviCRM into operating on the test database, but as of this writing, I haven't found an elegant way to do so.
* A similar method could be used to duplicate the configured Drupal application. Instead of calling parent::setUp(), the method would duplicate everything that function does except run the install, and instead copy every table into the test database.

Comments
Post new comment