Running groups of Drupal tests from the command line

I’ve written about using drush to evaluate PHP statements in the Drupal context using the command line before, and it turns out that Drush is also quite useful for running Simpletest scripts. Drush comes with a module that allows you to display all the available tests with “drush test list”, run all the tests with “drush test run”, or run specified tests with “drush test run test1,test2″.

‘Course, I wanted to run groups of tests and tests matching regular expressions, so I defined two new commands:

drush test run re regular-expression
Run all tests matching a regular expression that uses ereg(..) to match.
Ex: drush test run re Example.*
drush test run group group1,group2…
Run all tests matching the given groups
Ex: drush test run group Example

Here’s the patch to make it happen:

Index: drush_simpletest.module
===================================================================
--- drush_simpletest.module	(revision 884)
+++ drush_simpletest.module	(working copy)
@@ -12,9 +12,13 @@
 function drush_simpletest_help($section) {
   switch ($section) {
       case 'drush:test run':
-        return t("Usage drush [options] test run.\n\nRun the specified specified unit tests. If  is omitted, all tests are run.  should be a list of classes separated by a comma. For example: PageCreationTest,PageViewTest.");
+        return t("Usage drush [options] test run .\n\nRun the specified unit tests. If  is omitted, all tests are run.  should be a list of classes separated by a comma. For example: PageCreationTest,PageViewTest.");
       case 'drush:test list':
         return t("Usage drush [options] test list.\n\nList the available tests. Use drush test run command to run them. ");
+      case 'drush:test group':
+        return t("Usage drush [options] test group .\n\nRun all unit tests in the specified groups. For example: drush test group Group1,Group2");
+      case 'drush:test re':
+        return t("Usage drush [options] test re .\n\nRun all unit tests matching this regular expression. For example: drush test re Page.*");
   }
 }
 
@@ -30,10 +34,18 @@
     'callback' => 'drush_test_list',
     'description' => 'List the available Simpletest test classes.',
   );
+  $items['test re'] = array(
+    'callback' => 'drush_test_re',
+    'description' => 'Run one or more Simpletest tests based on regular expressions.',
+  );
+  $items['test group'] = array(
+    'callback' => 'drush_test_group',
+    'description' => 'Run one or more Simpletest test groups.',
+  );
   return $items;
 }
 
-function drush_test_list() {
+function drush_test_get_list() {
   simpletest_load();
   // TODO: Refactor simpletest.module so we don't copy code from DrupalUnitTests
   $files = array();
@@ -60,6 +72,11 @@
       $rows[] = array($class, $info['name'], truncate_utf8($info['desc'], 30, TRUE, TRUE));
     }
   }
+  return $rows;
+}
+
+function drush_test_list() {
+  $rows = drush_test_get_list();
   return drush_print_table($rows, 0, TRUE);
 }
 
@@ -75,3 +92,31 @@
   }
   return $result;
 }
+
+function drush_test_re($expression) {
+  if (!$expression) {
+    die('You must specify a regular expression.');
+  }
+  $rows = drush_test_get_list();
+  $tests = array();
+  foreach ($rows as $row) {
+    if (ereg($expression, $row[0])) {
+      $tests[] = $row[0];
+    }
+  }
+  simpletest_run_tests($tests, 'text');
+  return $result;
+}
+
+function drush_test_group($groups) {
+  $rows = drush_test_get_list();
+  $tests = array();
+  $groups = explode(',', $groups);
+  foreach ($rows as $row) {
+    if (in_array($row[1], $groups)) {
+      $tests[] = $row[0];
+    }
+  }
+  simpletest_run_tests($tests, 'text');
+  return $result;
+}

That makes running tests so much easier and more fun!

  • http://sachachua.com Sacha Chua

    Submitted this at http://drupal.org/node/297070 – my very first patch!

  • http://robshouse.net Robert Douglass

    Looks like you got your very first blunt response, too. Ah, well, such is the life of a module maintainer. If you’re maintaining 3 branches of a module, and someone posts a patch for a feature to the oldest of the three, you’re not likely to take the time to port it to the other branches. Why? Because you’re too busy (all module maintainers are, believe me.) So now what does Sacha do? Here are the options (note that the occasional rant about the bluntness of module maintainers is assumed…)

    1. Sacha can be happy that she’s got working code and not bother to port to D6 and D7. After all, the patch is a gift to the world, right? Can’t people be a little more thankful?

    2. Sacha can port the path to D6 and D7. It’s only 20-30 LOC, probably not needing many if any changes. But there is a hook_help in there (gotta look that one up, can’t remember if it changed), and the rest is internal Drush API. Wonder where Drush API changes between versions are documented? So, with installation of two new Drupal versions, a modicum of testing, rerolling the patch etc., 1-3 hours?

    3. Sacha can hope that someone using D6 or D7 and Drush reads this post and wants the feature so much that *they* do the ports. It’s all about itches and scratches, right?

    Of course, the most common choice is #1. This usually means that sometime in a year or so, when Sacha is doing the next Drupal project (this time on D6), she’s gotta dig up this patch, port it, and account for any other changes that have happened in the meantime. Or the patch simply dies and becomes lost forever.

    Everybody hopes Sacha will pick #2 because that’s really the only way to guarantee that this wasn’t a waste of time. The effort to make the patch, write the post and submit the patch – it would be a shame if the patch didn’t live on and it turned into “I tried… at least it works for me”

    The occasional miracle happens and a suitor for #3 comes along, but don’t hold your breath.

  • http://www.krisbuytaert.be/blog/ Kris Buytaert

    The link you post to drush is wrong.
    It should be http://drupal.org/project/drush , that’s project/ not projects/ ;)