SimpleTest example module notes

The project I'm working on nicely manages to involve segments of drupal that I didn't learn as much about with the last project. I'm going back and working through the relevant submodules of the examples module to try to get a clear understanding of the drupalish way to handle things. The first thing on my list is simpletest. This is something I could/should have learned for the last project, but I had so much going on that I never got to it. It probably would have saved me time if I'd been disciplined, so I'll try to be more test-driven this time.

SimpleTest

Drupal 7, and I think drupal 6 and 8 a well, have both functional tests and unit tests. Functional tests spin up an entire test environment complete with database tables and a file directory. You can actually load pages and test for content on them, and things like that. Unit tests are for testing discreet bits of functionality that don't require drupal's chrome running. Kind of like testing your module's php underpinnings.

Functional Testing (DrupalWebTestCase)

Here is the chrome for a DrupalWebTestCase:

/**
 * The MyModuleTestCase is a functional test case, meaning that it
 * actually exercises a particular sequence of actions through the web UI.
 *
 * @ingroup simpletest_example
 */
class MyModuleTestCase extends DrupalWebTestCase {

  protected $privilegedUser;

  /**
   * Give display information to the SimpleTest system.
   *
   * getInfo() returns a keyed array of information for SimpleTest to show.
   *
   * It's a good idea to organize your tests consistently using the 'group'
   * key.
   */
  public static function getInfo() {
    return array(
      'name' => 'MyModule Example',
      'description' => 'Your test description here.',
      'group' => 'Custom',
    );
  }

  /**
   * Set up the test environment.
   *
   * This method is called once per test method, before the test is executed.
   * It gives you a chance to control the setup of the test environment.
   *
   * If you need a different test environment, then you should create another
   * test class which overloads DrupalWebTestCase::setUp() differently.
   *
   * @see DrupalWebTestCase::setUp()
   */
  public function setUp() {
    // We call parent::setUp() with the list of modules we want to enable.
    // This can be an array or just a list of arguments.
    parent::setUp('mymodule');
    // Create and log in our user. The user has the arbitrary privilege
    // 'extra special edit any simpletest_example' which is provided by
    // our module to grant access.
    $this->privilegedUser = $this->drupalCreateUser(array('create simpletest_example content', 'extra special edit any simpletest_example'));
    // obviously the line above isn't exactly relevant to any case, but it creates a user with some set of permissions.
    $this->drupalLogin($this->privilegedUser);
  }

  /**
   * Here's an example of testing to see that articles can be created
   */
  public function testMyModuleCreate() {
    // Create node to edit.
    $edit = array();
    $edit['title'] = $this->randomName(8);
    $edit["body[und][0][value]"] = $this->randomName(16);
    $this->drupalPost('node/add/article', $edit, t('Save'));
    $this->assertText(t('Article @title has been created.', array('@title' => $edit['title'])));
  }
}

Unit Testing (DrupalUnitTestCase)

Unit testing requires inheriting from class DrupalUnitTestClass. It requires the same getInfo() and setUp() methods. Inside setup you need to drupal_load the module instead of enabling the necessary modules, because the we don't load up all the drupal system for unit tests.

The chrome:

/**
 * No environment is provided to a test case based on DrupalUnitTestCase;
 * it must be entirely self-contained.
 *
 * @ingroup simpletest_example
 */
class MyModuleUnitTestCase extends DrupalUnitTestCase {

  /**
   * 
   */
  public static function getInfo() {
    return array(
      'name' => 'MyModule unit tests',
      'description' => 'Description here.',
      'group' => 'Custom',
    );
  }

  /**
   * Set up the test environment.
   *
   * Note that we use drupal_load() instead of passing our module dependency
   * to parent::setUp(). That's because we're using DrupalUnitTestCase, and
   * thus we don't want to install the module, only load it's code.
   *
   * Also, DrupalUnitTestCase can't actually install modules. This is by
   * design.
   */
  public function setUp() {
    drupal_load('module', 'mymodule');
    parent::setUp();
  }

  /**
   * Note that no environment is provided; we're just testing the correct
   * behavior of a function when passed specific arguments.
   */
  public function MyModuleUnitTestExampleFunction() {
    $result = mymodule_empty_mysql_date(NULL);
    // Note that test assertion messages should never be translated, so
    // this string is not wrapped in t().
    $message = 'A NULL value should return TRUE.';
    $this->assertTrue($result, $message);

    $result = simpletest_example_empty_mysql_date('');
    $message = 'An empty string should return TRUE.';
    $this->assertTrue($result, $message);
  }
}

Other testing notes

One test file would best be in the module root with name mymodule.test and it needs to be mentioned in the ,info file's files[] array. For multiple test files they should still have the .test extension, and be in the files[] array, but a folder called tests is a good place for them

You can also create a parent class to do all the setup for a child class that inherits from your parent class. this would allow to you skip all the common setup in the child class because it will have been done by the parent

Add new comment

Filtered HTML

  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.