×
×

15 minutes to your first Drupal integration test

This post will help you write and run your first Drupal integration test using Red Test framework in less than 15 minutes. By end of this post, you will be able to write an automated test to make sure that superuser is able to create two pieces of content, one published and the other unpublished. We'll test that anonymous user is able to view the published content and is not able to view the unpublished content. You can follow along these steps on any Unix or Mac machine and have automated tests running in a matter of minutes.

1) Choose any Drupal 7 site that you have running on your machine.

2) Go through the Red Test installation post and install Red Test in this site. You can skip the last step in this post where we run the tests since the test suite that comes by default is the one meant for Standard installation profile of Drupal and will invariably fail with your custom site.

3) Create a new content type called "Test Me". Note that machine name of the content type should be "test_me". This is important because Red Test identifies content based on its machine name and not its label.

4) Remove the body field from the "Test Me" content type and add a new field: "Test Me Body" (machine name: field_test_me_body). It should be a "Long Textarea With Summary" field.

5) First we need to inform Red Test about the "Test Me" content type. Go to <DRUPAL_ROOT>/tests/folder/RedTest/entities/Node folder.

cd <DRUPAL_ROOT>/tests/RedTest/entities/Node

Create a new PHP file named TestMe.php. Create a class called TestMe which extends Node class.

<?php

namespace RedTest\entities\Node;

use RedTest\core\entities\Node;

/**
 * Class TestMe
 *
 * @package RedTest\entities\Node
 */
class TestMe extends Node {

}

6) We need to define the node add form for "Test Me" content type as well. Go to <DRUPAL_ROOT>/tests/RedTest/forms/entities/Node folder.

Create a new PHP file named TestMeForm.php. Create a class called TestMeForm which extends NodeForm.

<?php

namespace RedTest\forms\entities\Node;

use RedTest\core\forms\entities\Node\NodeForm;

/**
 * Class TestMeForm
 *
 * @package RedTest\forms\entities\Node
 */
class TestMeForm extends NodeForm {

}

We are done defining the configuration. Let's start writing the test.

7) Go to <DRUPAL_ROOT>/tests/RedTest/tests/entities/node and create a folder named "test_me". Under that folder, create a folder named "crud" and switch to that folder.

cd <DRUPAL_ROOT>/tests/RedTest/tests/entities/node
mkdir -p test_me/crud
cd test_me/crud

8) Create a file named AnonymousUserTest.php. This is the file where we'll be writing our test. Red Test is based on PHPUnit so if you know PHPUnit, this will be extremely easy for you. Create a class named AnonymousUserTest that extends RedTest_Framework_TestCase. In the setupBeforeClass() method, we'll log in as Superuser (uid = 1) and create two pieces of content of the type "Test Me", one published and the other unpublished. Then we'll test the following:

  • Anonymous user has access to view the published content.
  • Published content that the anonymous user sees has "field_test_me_body" in it when viewing in "full" view mode.
  • Anonymous user does not have access to view the unpublished content.

Below is the code which tests the above scenarios. The code is well-commented so it should be easy to understand what's going on.

<?php

namespace RedTest\tests\entities\node\test_me\crud;

use RedTest\core\entities\User;
use RedTest\core\RedTest_Framework_TestCase;
use RedTest\entities\Node\TestMe;
use RedTest\core\Menu;
use RedTest\core\Path;

/**
 * Class AnonymousUserTest
 *
 * @package RedTest\tests\entities\node\test_me\crud
 */
class AnonymousUserTest extends RedTest_Framework_TestCase {
  /**
   * @var TestMe
   */
  private static $publishedTestMeContent;

  /**
   * @var TestMe
   */
  private static $unpublishedTestMeContent;

  /**
   * Create a published and an unpublished content of the type
   * "Test Me".
   */
  public static function setupBeforeClass() {
    $options = array(
      'required_fields_only' => FALSE,
      'status' => 'published',
    );

    // Log out the user just to make sure that there is no
    // logged in user at this point.
    User::logout();

    // Log in as user 1.
    $userObject = User::loginProgrammatically(1)
      ->verify(get_class());

    // Create a published TestMe content with all its fields
    // filled with random values.
    self::$publishedTestMeContent = 
      TestMe::createRandom(1, $options)->verify(get_class());
    
    // Create an unpublished TestMe content with all its
    // fields filled with random values.
    $options['status'] = 'unpublished';
    self::$unpublishedTestMeContent =
      TestMe::createRandom(1, $options)->verify(get_class());

    // Log the user out. After this step, the user will be 
    // anonymous.
    $userObject->logout();
  }

  /**
   * Make sure that anonymous user has access to view the
   * published "Test Me" content.
   */
  public function testPublishedViewAccess() {
    // Use node_access() function to check whether user has
    // access to view the published content.
    $access = self::$publishedTestMeContent->hasViewAccess();
    $this->assertTrue($access,
      'Anonymous user does not have permission to view a
      published "Test Me" content.');

    // Use Menu class' hasAccess() function to check whether
    // user has access to node/<nid> page. This is a more
    // general function and can be used for custom paths as
    // well.
    $id = self::$publishedTestMeContent->getId();
    $path = new Path('node/' . $id);
    $access = $path->hasAccess();
    $this->assertTrue($access,
      'Anonymous user does not have permission to view a
      published "Test Me" content.');
  }

  /**
   * Make sure that anonymous user is able to view the
   * published "Test Me" content in "full" and "teaser" view
   * modes.
   */
  public function testPublishedView() {
    // Get the renderable array of the node in "full" view
    // mode. Note that a node in "full" view mode does not
    // have title inside it. The title is being rendered by
    // page tpl.
    $view = self::$publishedTestMeContent->view('full');
    $this->assertArrayHasKey('field_test_me_body', $view,
      'Anonymous user is not able to view the body field of
      the published content in "full" view mode.');
  }

  /**
   * Make sure that anonymous user does not have access to
   * view the published "Test Me" content.
   */
  public function testUnpublishedViewAccess() {
    // Use node_access() function to check whether user has
    // access to view the unpublished content.
    $access =
      self::$unpublishedTestMeContent->hasViewAccess();
    $this->assertFalse($access,
      'Anonymous user has permission to view an unpublished
      "Test Me" content.');

    // Use Menu class' hasAccess() function to check whether
    // user has access to node/<nid> page. This is a more
    // general function and can be used for custom paths as
    // well.
    $id = self::$unpublishedTestMeContent->getId();
    $path = new Path('node/' . $id);
    $access = $path->hasAccess();
    $this->assertFalse($access,
      'Anonymous user has permission to view an unpublished
      "Test Me" content.');
  }
}

9) Now that we have the test set up, we want to inform Red Test that this is the test that needs to be run and not any other test that comes by default. Open <DRUPAL_ROOT>/tests/phpunit.xml file and in the directory tag under testsuite, write "./RedTest/tests/entities/node/test_me". Here is how the phpunit.xml file will look:

<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.6/phpunit.xsd"
         backupGlobals="false"
         backupStaticAttributes="false"
         cacheTokens="false"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         forceCoversAnnotation="false"
         mapTestClassNameToCoveredClassName="false"
         processIsolation="false"
         stopOnError="true"
         stopOnFailure="true"
         stopOnIncomplete="false"
         stopOnSkipped="false"
         verbose="true">
    <testsuites>
        <testsuite name="ParaTest Fixtures">
            <directory>./RedTest/tests/entities/node/test_me</directory>
        </testsuite>
    </testsuites>
</phpunit>

10) Now is the time to execute the test. Go to <DRUPAL_ROOT> folder and run the test.

cd <DRUPAL_ROOT>
tests/bin/paratest --phpunit=tests/bin/phpunit --processes=4 --log-junit="tests/output.xml" --configuration=tests/phpunit.xml

You should see the following output:

Running phpunit in 4 processes with tests/bin/phpunit

Configuration read from /Users/neeravm/Sites/drupal7/tests/phpunit.xml

...

Time: 780 ms, Memory: 30.50Mb

OK (3 tests, 5 assertions)

You will notice that the test completed in less than a second, more specifically in 780 ms. The same test would have taken a few seconds to run using Simpletest or Behat. Although a few seconds don't seem like much, it quickly adds up. Also Red Test is designed to run multiple scenarios in parallel and it doesn't bootstrap Drupal for each test separately. Currently we have just one test. So the speed-up by using Red Test will be even more when we add more tests.

If you did go through the example on your system and got stuck somewhere, please write it in the comment with your email id or email me at admin@redcrackle.com. I can help you with the setup. If the tests passed, then write a comment and note down how much time it took to run the test!

 

Related Article:

Testing blocks using Red Test

Services: 
Drupal Development

Sign up for our weekly newsletter


Comments

  • by RAUNAK SINGH (not verified)
  • Thu, 11/26/2015 - 10:56

I want to know how can we write test cases for service api or service module in drupal 7 plzz help me

neerav.mehta's picture
  • by neerav.mehta
  • Fri, 11/27/2015 - 07:37

The easiest way is to invoke the Service page callback directly in the test with appropriate input parameters. Email us on admin [AT] redcrackle [dot] com if you need more specific directions.

  • by RAUNAK SINGH (not verified)
  • Tue, 12/01/2015 - 23:03

Hello sir

I want now how we can write test cases for my custom service module or drupal custom modules plzzz send me some example links how can we test our custom modules using RedTest Framework

  • by RAUNAK SINGH (not verified)
  • Tue, 12/01/2015 - 23:03

Hello sir

I want now how we can write test cases for my custom service module or drupal custom modules plzzz send me some example links how can we test our custom modules using RedTest Framework

neerav.mehta's picture
  • by neerav.mehta
  • Thu, 12/03/2015 - 06:58

Raunak, what exactly do you want to test? If you can give me an idea, I can send you relevant examples. You can email me at admin [AT] redcrackle [dot] com.

  • by RAUNAK SINGH (not verified)
  • Thu, 12/10/2015 - 01:42

Sir, Actually In My custom module, I rendered a node form of existing content type in menu callback. I just want to test when I hit that URL then I get particular that node form. After that I want to submit this form with custom value. How will I achieve this in Red test cases.

In simple test case, I successfully tested it but execute of this test case is quite time consuming.

  • by RAUNAK SINGH (not verified)
  • Thu, 12/10/2015 - 01:43

Sir, Actually In My custom module, I rendered a node form of existing content type in menu callback. I just want to test when I hit that URL then I get particular that node form. After that I want to submit this form with custom value. How will I achieve this in Red test cases.

In simple test case, I successfully tested it but execute of this test case is quite time consuming.

  • by Chris Wells (not verified)
  • Thu, 12/29/2016 - 11:41

None of paratest is working at all, not with the 15 minute example, or even with the built-in tests. Just gives me a non-zero exit code, and no other output.

Add new comment