Community Documentation

SimpleTest Tutorial (Drupal 7)

Last updated May 23, 2013. Created by puregin on August 23, 2010.
Edited by ressa, geerlingguy, wizonesolutions, alextataurov. Log in to edit this page.

Note: The code for this tutorial is maintained in the Examples for Developers module. This means:

  • You can grab a copy there and fiddle with it, change it, experiment with it.
  • If you find problems, file an issue there and get it fixed. Patches and improvements are welcome.

This tutorial will take you through the basics of testing in Drupal. By the end you should be able to write your first test! For this example we will create a dummy module called "simpletest_example", which provides a content type called "simpletest_example". This content type is exactly the same as any basic Drupal node type (e.g., 'page'). The tutorial will then explain how to test this simpletest_example content type to ensure it functions properly.

Setup for the tutorial

First, we will need to make sure that the Simpletest module is installed. In Drupal 7, Simpletest is part of the core and is called Testing. If you have not done so already, you will need to make sure the Simpletest module is enabled.

Simpletest verbose testing information is on by default in Drupal 7, but since you need it, you may want to check to make sure it's turned on. It gives you a screenshot of what the Drupal page looks like at every point in the test. Check it at admin/config/development/testing/settings.

This tutorial makes use of the Simpletest Example module from http://drupal.org/project/examples.

How Drupal's Simpletest works

Most of Drupal is web-oriented functionality, so it's important to have a way to exercise these functions. Simpletest creates a complete Drupal installation and a virtual web browser and then uses the virtual web browser to walk the Drupal install through a series of tests, just like you would do if you were doing it by hand. It's terribly important to realize that each test runs in a completely new Drupal instance, which is created from scratch for the test. In other words, none of your configuration and none of your users exists! None of your modules are enabled beyond the default Drupal core modules. If your test sequence requires a privileged user, you'll have to create one (just as you would if you were setting up a manual testing environment from scratch). If modules have to be enabled, you have to enable them. If something has to be configured, you'll have to use Simpletest to do it, because none of the configuration on your current site is in the magically created Drupal instance that we're testing. None of the files in your files directory are there, none of the optional modules are installed, none of the users are created. We have magic commands to do all this within the Simpletest world, and we'll get to that in a little bit.

About the Simpletest Example module

The Simpletest Example module provides a custom node type (like 'page' or 'story'). It has a title and a body. That's it. It gives us a chance to demonstrate testing of content creation. To implement the node, we have to provide the node type to Drupal with hook_node_info() and provide a form for the node type with hook_form(). We implement permissions for the module (so that you need "create simpletest_example content" permissions to create one, or "edit own simpletest_example content" permissions to edit one.) And of course we need a simpletest_example.info.

Note that our module has a bug in it: The permissions handling is not done correctly. So even though there's a permission string for 'edit own simpletest_example', it's not handled correctly by simpletest_example_access(), so when our properly privileged user tries to edit a node, it can't. Of course, the manual user tester was probably testing with user 1, so never saw this failure case. We'll get to this later.

This code is maintained in the Examples for Developers module. It really helps to try out the code, change it a bit, experiment with dummy code like this before trying to do something serious.
You're encouraged to grab it, enable the module, and work with the code.

Figuring out what we need to test

If you install simpletest_example, you can manually go through the steps and see what you think needs to be tested.

Visit Content > Add new content > Simpletest Example Node Type where you should see the following.

simpletest_example content type

Look at the interface and identify the things that need to be tested in our node type.

simpletest_example content type identify

Take a tour and make sure you are familiar with how the interface works and that it functions properly in a basic case where you are manipulating it instead of simpletest. If you don't understand what it's doing or why, you can't write a workable test. In this case fill in the title and body fields and click the save button. You should see something like the following.

simpletest_example content type submit

Building a test for simpletest_example

Now it's time to create our tests, which we'll do in the simpletest_example.test file (which you have in the complete module download).

If you are adding a new .test file to a module for Drupal 7, you will have to add it to the files[] section of the module's .info file. In our example, there is an entry for simpletest_example.test in the files[] section of the simpletest_example.info file:

files[] = simpletest_example.test

If you add a .test file to an existing module, you will probably need to rebuild Drupal's caches in order to notify Drupal of the new file. To rebuild all caches, you can go to admin/config/development/performance and click the "Clear all caches" button.

There are four basic steps involved in building a test:

  • Creating the structure (just creating a class that inherits from DrupalWebTestCase)
  • Initializing the test case with whatever user creation or configuration needs to be done
  • Creating actual tests within the test case
  • And, of course, trying desperately to figure out why our test doesn't work the way we expect, and debugging the test (and perhaps the module)

To start, we just need a bit of boilerplate extending DrupalWebTestCase.

<?php
/**
* Tests the functionality of the Simpletest example content type.
*/
class SimpletestExampleTestCase extends DrupalWebTestCase {
  protected
$privileged_user;

}
?>

To make the test available to the Simpletest testing interface, we implement getInfo(). This just provides the user interface information that will show up on the simpletest page after clearing the cache table.

<?php
 
public static function getInfo() {
   
// Note: getInfo() strings are not translated with t().
   
return array(
     
'name' => 'Simpletest Example',
     
'description' => 'Ensure that the simpletest_example content type provided functions properly.',
     
'group' => 'Examples',
    );
  }
?>

Next comes the terribly important setUp(). Here is where we must do anything that needs to be done to make this Drupal instance work the way we want to. We have to think: "What did I have to do to get from a stock Drupal install to where I can run this test?". In our case, we know that we had to:

  • Enable the Simpletest Example module
  • Create a user with privileges to create a simpletest_example node
  • Log in the user

This work is done by the setUp() method:

<?php
 
public function setUp() {
   
// Enable any modules required for the test. This should be an array of
    // module names.
   
parent::setUp(array('simpletest_example'));
   
// Create and log in our privileged user.
   
$this->privileged_user = $this->drupalCreateUser(array(
     
'create simpletest_example content',
     
'extra special edit any simpletest_example',
      ));
   
$this->drupalLogin($this->privileged_user);
  }
?>

Note: In Drupal 6, we had to explicitly enable every dependency our module had. Drupal 7 automatically enables all dependencies.

Create specific test: Creating a node

Now we need to create specific tests to exercise the module. We just create member functions of our test class, each of which exercises a particular test. All member functions should start with 'test' in lower-case. Any function that starts this way will automatically be recognized by Simpletest and run when requested (Note: Although it is possible to put each assertion into a separate function, testing several different assertions at once is recommended instead).

Our first test will be to create a new simpletest_example node by using the form at node/add/simpletest-example:

<?php
 
/**
    * Tests creation of a Simpletest example node.
    */
 
public function testSimpleTestExampleCreate() {
   
// Create node to edit.   
   
$edit = array();
   
$edit['title'] = $this->randomName(8);
   
$edit["body[und][0][value]"] = $this->randomName(16);
   
$this->drupalPost('node/add/simpletest-example', $edit, t('Save'));
   
$this->assertText(t('Simpletest Example Node Type @title has been created.', array('@title' => $edit['title'])));
  }
?>

drupalPost, drupalGet, and Assertions

The code above did a very simple form submission on the node/add/simpletest-example page. It prepares an array of fields (the $edit array, giving random values for the title and body) and then it POSTs the form and asserts that we find appropriate text on the page.

Most tests will follow this pattern:

  1. Do a drupalGet() to go to a page or a drupalPost() to POST a form.
  2. Do one or more assertions to check that what we see on the page is what we should see.

$this->drupalGet($path) is as easy as it can be: It just goes to the named page.

$this->drupalPost($path, $edit_fields, $submit_button_name) is only slightly more complex.

And then there are dozens of possible assertions. The easiest of these is $this->assertText($text_to_find_on_page). When you get beyond this tutorial, you'll want to read about more of them.

Users in test and the core environment

Tests run in a separate (sandbox) environment. Logging a user in with $this->drupalLogin($account) happens in the test environment. Test methods, like drupalGet and drupalPost also act in that sandbox. If you want to call core API functions (eg: user_access) it's highly recommended to assign the test user to the core environment:

<?php
$account
= $this->drupalCreateUser(array('access content'));
$this->drupalLogin($account);
global
$user;
$user = user_load($account->uid);
$this->assertFalse(user_access('access content'));
?>

Simpletest switches the user to uid 1 during testing and takes care of restoring the user so we can change the user without security concerns.

Running the Simpletest web interface

Next we need to run the test. Here we'll use the web interface to run the test.

Go to the Configuration > Development > Testing page and find the module that you created. On this page you will see two tabs, Settings and List. In the List tab, you will see a list with the available tests. Select the test you just created -- it will be in the "Examples" group -- and press the Run tests button. (You may need to clear Drupal's cache to see it in the list),

Once the test has run you should see the results, which for this test will pass.

You can also run tests from command line. More information about it can be found at Running Tests Through command-line.

A demonstration failing test

It really doesn't teach us much to just have a test that succeeds. Let's look at one that fails.

We'll work within the same class and this time try to test editing a node. Our module has a flaw in it - it doesn't handle the 'edit own simpletest_example' permission string correctly, so a user with only that permission will not be able to edit it. In this test we'll create a node and then try to edit it.

Run the test and see the result.

<?php
 
/**
    * Tests editing a Simpletest example node.
    */
 
public function testSimpleTestExampleEdit() {
   
$settings = array(
     
'type' => 'simpletest_example',
     
'title' => $this->randomName(32),
     
'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))),
    );
   
$node = $this->drupalCreateNode($settings);

   
// For debugging, we might output the node structure with $this->verbose()
   
$this->verbose('Node created: ' . var_export($node, TRUE));
   
// It would only be output if the testing settings had 'verbose' set.

    // We'll run this test normally, but not on the testbot, as it would
    // indicate that the examples module was failing tests.
   
if (!$this->runningOnTestbot()) {
     
// The debug() statement will output information into the test results.
      // It can also be used in Drupal 7 anywhere in code and will come out
      // as a drupal_set_message().
     
debug('We are not running on the PIFR testing server, so will go ahead and catch the failure.');
     
$this->drupalGet("node/{$node->nid}/edit");
     
// Make sure we don't get a 401 unauthorized response:
     
$this->assertResponse(200, 'User is allowed to edit the content.');

     
// Looking for title text in the page to determine whether we were
      // successful opening edit form. Note that the output message
      //  "Found title in edit form" is not translated with t().
     
$this->assertText(t("@title", array('@title' => $settings['title'])), "Found title in edit form");
    }
  }
?>

Unit Testing

Simpletest also provides a DrupalUnitTestCase as an alternative to the DrupalWebTestCase.

The database tables and files directory are not created for unit tests. This makes them much faster to initialize than functional tests but means that they cannot access the database or the files directory. Calling any Drupal function that needs the database will throw exceptions. These include routine functions like watchdog(), module_implements(), module_invoke_all() etc.

More information about writing unit tests for Drupal can be found at Unit Testing with Simpletest.

When to use t() in simpletests

Unlike most strings in Drupal, strings in simpletest should not always be enclosed in t(). This is for two reasons:

  1. Most strings used in simpletest are for testing only and not used on any website, so they are not translated.
  2. It is simpler to provide performant, robust test coverage when different components are decoupled as much as possible (which means not running code like t() from other subsystems when it is not needed).

When to use t()

Use t() in your test methods in the following cases:

  • When you are specifically testing functionality related to translation and localization.
  • When you are testing for the presence of a string that is already translated in the user interface. For example, to check for the presence of the "Access denied" message on a 403 page, use the following code:
    <?php
    $this
    ->assertText(t('Access denied'), 'Access denied message found on forbidden foo page.');
    ?>

    Note that the first string, 'Access denied', is provided by drupal_deliver_html_page() and translated there with t(). Therefore, we also use t() when checking for the presence of this message.

When not to use t()

Do not use t() in:

  • Strings in getInfo().
  • Test assertion messages. Repeating the example from above:
    <?php
    $this
    ->assertText(t('Access denied'), 'Access denied message found on forbidden foo page.');
    ?>

    The second string, 'Access denied message found on forbidden foo page.', is for the test runner's information only, and so it is not translated.
  • Test module and theme output, unless the test module is intended to test translation functionality.

In all the cases above, you can use format_string() if you need to format and sanitize variables for output in the string.

Debugging Simpletests

In the testing settings (admin/config/development/testing/settings) there is an option "Provide verbose information when running tests ". If you turn this on, every drupalGet() and every drupalPost() will be captured as an HTML file, which will be available for you to view in the test results. This is a tremendously important tool.

You can also use $this->verbose("some message") and the message you provide will be shown when verbose information is being displayed.

More information on debug() and $this->verbose() can be found on Boombatower's blog about debugging in Drupal 7.

Where to go from here

  • Several of the API functions and assertions are documented in this section.
  • Reading through drupal_web_test_case.php is instructive

Note: A Google Docs presentation to go with this material is maintained by rfay.

Comments

External Login Users

The module I am working from has the following login alteration:

<?php
user_external_login_register
( $username, 'mymodule_authentication' );
?>

Therefore, I have to have user information that gets sent through the login behavior so that the external system validates the user. Thus allowing the further functions to be used within Drupal.

How can we leverage the Drupal test behavior to force user identifier information such as an email that gets sent to the backend?

This was also asked in previous threads by other people, but for some reason:

<?php
    $this
->privileged_user = $this->drupalCreateUser(array('create simpletest_example content', 'extra special edit any simpletest_example'));
   
$this->drupalLogin($this->privileged_user);
?>

only handles permissions. But can't we test on a specific user if we want to? It is fine if it isn't part of what is written in test stone. Just need to find a way to override the following from test scripts:

<?php
user_external_login_register
( $username, 'mymodule_authentication' );
?>

Testing from an installation profile

If you need to test a module as part of an installation you need to add the code below to enable the right one. If not enabled, the modules in the installation profile won't be found.

<?php
class MyTestCase extends DrupalWebTestCase
 
// Needed to be able to test the module inside an installation profile
 
protected $profile = 'my_profile';
  ...
?>

testing_api directive in .info file

Thank you for this tutorial.
But, following your instructions, I was unable to see my custom test cases on the Testing list page ... actually, even test cases from other contrib modules were not visible!

After looking in SimpleTest .info file, I realized that I need to add the directive "testing_api = 2.x" in my .info file, in order to store my .test file info in the registry table. And now I can see my custom test cases.

Am I the only one with this problem ?

Web project management
www.webismymind.com

Mine were located just fine

As long as my test files were identified in my .info file my tests have been located just fine after clearing the cache.

Hi, thanks so much for this

Hi, thanks so much for this comment! We were having the same problem, but adding the testing_api directive worked a treat.

Thanks Man

Had the same problem and what you posted solved it - thanks a bunch!

Thanks

Thanks for the information this helped. Once I added the directive "testing_api = 2.x" to the info file it worked.

Thanks

Hi webismymind,

I'm also facing the same problem, I will try your solution ASAP and lets see whether it works for me or not. Thanks in advance.

Thanks & Regards,
Soumya Sona Das

Great!

Great! This is also working with me. Thanks again.

Thanks & Regards,
Soumya Sona Das

When setting form values, a

When setting form values, a nice reminder for something that cost me a bit of time is setting the value of textarea's

$form types 'textfield' you can just

  $edit['title'] = "string value";

however type 'textarea' , you need to consider the multilanguage functionality of D7 and set it to something like

  $edit["body[und][0][value]"] = "string value";

'und' means undefined language (default) otherwise you'll end up with Failed to set field bodyfield

Slight Amend

"Go to Modules page and find the module that you created and go to Configure." should be "Go to Modules page and find the Testing module and go to Configure.".

Attention! Testing for permissions!!

There is a *really* annoying inconstancy in how to create a user with certain permissions, $this->drupalCreateUser(..) act's VERY different to drupalPost'ing a set of permissions into admin/people/permissions.

drupalCreateUser(array("some new permission") will always return "permission not found" for *NEW* permissions, you *MUST* do a drupalPost into admin/people/permissions

Drupal SimpleTest

CORRECT method

    $edit = array();
    $rid = 2;
    $test_type_name = "mynewtype";
    $edit["{$rid}[create {$test_type_name} content]"] = true;
    $edit["{$rid}[edit any {$test_type_name} content]"] = true;
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
    drupal_static_reset('user_access');
    drupal_static_reset('user_role_permissions');
    $this->assertText(t('The changes have been saved.'), t('Successful save message displayed.'));

INCORRECT method

  $this->admin_user = $this->drupalCreateUser(array(
      'administer content types',
      'administer permissions',
      'create mynewtype content',
      'edit any mynewtype content'
      ));

very annoying!

Handling cookies in simpletest

If you need to set a cookie to test some functionality

     $this->additionalCurlOptions[CURLOPT_COOKIE] = 'mycookiename='.rawurlencode($cookievalue) . ';';      

Note: You may need to include the existing SESSID, but I'm not sure the best way just yet

DO NOT CHANGE YOUR USER AGENT STRING!

Here is a really good way to waste your employers time

Unless I'm missing something obvious, if you set the User-Agent to something else then you might suddenly notice that your database has disappeared or for example $this->drupalGet("node/1") returns '404' when earlier in your test it worked.

For example;

    $this->additionalCurlOptions[CURLOPT_USERAGENT] = $test_bot_ua;

problem is, includes/common.inc relies on the User-Agent to switch database/table prefixes

from includes/common.inc

  // If the database prefix is being used by SimpleTest to run the tests in a copied
  // database then set the user-agent header to the database prefix so that any
  // calls to other Drupal pages will run the SimpleTest prefixed database. The
  // user-agent is used to ensure that multiple testing sessions running at the
  // same time won't interfere with each other as they would if the database
  // prefix were stored statically in a file or database variable.

And if you look at the tcpdump of the session

User-Agent: simpletest146234;1332462090;4f6bc20ad28958.38110168;hN5HgdF_lSDcf0m-kAb8ykqmlU7GZruT8f5yLq53_lM

Ouch! nice one!

SimpleTest throws AJAX HTTP error

Whatever I try to test with SimpleTest fails due to execution timeout. Every time I run a test, even this simpletest_example.test, I get a message similar to this:

An AJAX HTTP error occurred. HTTP Result Code: 200 Debugging information follows. Path: /annsjon_dev/?q=en/batch&id=39&op=do StatusText: OK ResponseText: Fatal error: Maximum execution time of 30 seconds exceeded in C:\xampp\htdocs\annsjon_dev\includes\database\select.inc on line 1243

Edit: What is different in these errors is the path of the fatal error, always something in core, almost always something regarding databases. That might imply there's something wrong with my databases but they are up to date according to the Status report.

I came to this tutorial thinking I would actually see a working example of SimpleTest. I'm quite new to Drupal but have managed to write my own module that I wanted to test properly before uploading it to the community.

Any help would be greatly appreciated!

AJAX HTTP error -- workaround

I have the same problem. I'm running Eclipse with XDebug under XAMPP.

As a workaround, I can get the tests to run correctly if I start the app from Eclipse using Debug. It works whether I use Eclipse's internal browser or the external Chrome browser.

I don't think it is a Drupal problem but something with my setup.

brion

I am also having this problem

I am also having this problem on a fairly straight forward site, all patched and up to date as of 19/04/2013. Running on Wamp Server. I extended my max_execution_time directive in my php.ini from 180 to 360 and it eventually ran one very simple test.

I don't for a minute think increasing the max execution time is the answer, but was a work around for me (at least for the duration of testing). It seems to be hanging on includes/database/database.inc line 2168, which is executing a query.

Contract Php & Drupal specialist

Failed to set the field to a value

Posted by ananth_kiran on May 14, 2012 at 1:06pm
hi
I have written a test file for testimg the fields.
The fields in the form are in a loop.
I have created a array type with the form fields value
and passed the array in the drupalPost method.
When I tried to test the case in the drupal
I was returned with error that
the "Failed to set field to " (all the field names
I set in the array and the values) in the test case result.
Here its happening only for the fields which are in loop.
For the remaining elements, success message is appearing.
I have tried in different ways but its giving the same result.
Can any one help me in this..
THANK YOU SO MUCH

ananth kiran m

Can we execute the queries in the .test file?

Can we execute the queries in the .test file?
I have written a test file for testimg the fields.
The fields in the form are in a loop.
I have created query and getting resultes from database and created a array type with the form fields value
and passed the array in the drupalPost method.
When I tried to test the case in the drupal
I was returned with no results in verbose message in the test case result.
Same query working in admin.inc file.
I have tried in different ways but its giving the same result.
Can any one help me in this..
THANK YOU SO MUCH

ananth kiran m

In the below code,
“$path = ‘admin/config/world/conf’;
$this->drupalPost($path, $edit, t('Save Detail')); ”

Drupal simpletest will browse the $path and post the $edit array values on the browsed page.
Is simpletest browse only static fields in the page?
or in other words,
Can it browse dynamic fields which are formed by query dynamically in the page?
when i send vaules to dynamic fields from .test file , I am getting the failure messages like
"Failed to set field to a value" for the dynamic fields.
I have tried many ways to resolve these issues.
Can anyone Please suggest me any solution
Thank you so much Friends.

Regards,
mak

ananth kiran m

Multiple unit test methods in a single testing class.

Note that it is possible to have multiple test methods within a single testing class. The following is from the "Unit Testing with Simpletest" documentation, but appears to be equally applicable here:

All unit test methods should start with 'test' in lower-case. Any method that starts this way will automatically be recognized by Simpletest and run when requested. Although it is possible to put each assertion into a separate method, testing several different assertions at once is recommended instead.

Testing Fields API

I am new to Drupal 7 (I can be mistaken), but it took me literally almost 3 days to figure out that you have to flush internal static cache of the function _field_info_collate_fields() if you are having troubles with Fields API.

Basically what I was doing in my tests is assigning a field to a taxonomy term, creating a taxonomy term via user interface and posting some value in my just assigned field. I was expecting to see that field's value on Term View page but it never appeared there unless you flush the cache of that function. Have I missed something out or this quite awkward way is really the only way to make your tests work?

http://takearest.ru - my Drupal baby :)

Fields? in the .test

Hi, i recently play with the example tests, but i dont know if when i use drupalget($path) o another methods i can test any field of the $content ( i want for example know the ['content']['system_main']['name']['#title'] ) how i get in the node template, but in the testing file i dont can get this field only. I saw that only is the html text and not the arrays how i do with devel in any page normaly.

There is any way for get any field and value in the testing file?

Thk in advance and regards.
Miguel

Aprendiendo siempre del grupo.
Saludos !!!

resolv Permission Error

hi, i resolv the Permission Error in this form

<?php
 
public function setUp() {
   
parent::setUp('simpletest_example');  // Enable any modules required for the test
     //create permissions
   
$perms = user_role_permissions(array(array_search('user_with_edit_permission'user_roles())=> 'user_with_edit_permission'));
   
$perms = array_keys($perms[array_search('user_with_edit_permission', user_roles())]);
   
$admin = $this->drupalCreateUser($perms);
   
$this->drupalLogin($admin);
   
//$this->drupalLogin($this->privileged_user);
 
}
?>

Using code sniffer with the

Using code sniffer with the Drupal coding standards reveals that instead of:
protected $privileged_user;
We should be using:
protected $privilegedUser;

Acquia