Dec 11 2018
Dec 11

Custom Post Types in WordPress

By Andrea Roenning

Custom post types are the key to taking a WordPress website from a simple blog to a robust system for managing many types of content and data. They help create a WordPress administrator experience which makes it easy for editors to add and edit repetitive content and gives the developer flexibility to create unique web pages to fit the site’s individual needs.

The Flexibility of Drupal 8

By Michael Miles

This article demonstrates six different methods of changing content and functionality in Drupal. Each method requires a different skill set and level of expertise, from non-technical inexperienced users to advanced Drupal developers. For each method, we describe the components, skills, knowledge, and limitations involved. The goal is to highlight Drupal’s flexibility as a Content Management framework.

How to Learn PHP Unit Testing With Katas

By David Hayes

Sometimes code is first tested when the unfortunate client or user feels like using the feature and tests the developer’s work. Hopefully, they’re not disappointed. That’s why you should be interested in unit testing and Test-Driven Development (TDD)—because it makes your life as a programmer better. It would have saved me hours of work in situations like those outlined in the last paragraph. TDD and automated testing let us focus on what we’re there for: solving complicated problems with code and leaving the computers to do more of the rest.

It’s About Time

By Colin DeCarlo

As applications scale and gain adoption, dates and time become much more of a concern than they once were. Bugs crop up, and developers start learning the woes of time zones and daylight saving time. Why did that reminder get sent a day early? How could that comment have been made at 5:30 a.m. if the post didn’t get published until 9:00 a.m.? Indiana has how man time zones?!

The Dev Lead Trenches: Creating a Culture

By Chris Tankersley

I have spent much time talking about creating and managing a working team, but there is one important piece I’ve left out of the puzzle until now—creating and crafting a culture that makes people want to work on your team and stay on your team. If you have a company culture which does not attract people, employees will be hard to find.

The Workshop: Producing Packages, Part Three

By Joe Ferguson

Over the past two months we’ve been building PHP Easy Math a purposely simple example library to demonstrate how to build a reusable package for the PHP ecosystem. Make sure to check the previous issues if you’re just now joining us! This article is the third and final installment in this series. We’re going to cover triaging and managing issues users may open as well as pull requests to your library.

Education Station: Interview Coding Challenges

By Edward Barnard

Meanwhile, the days where employees stay with the same company 20-30 years are long gone. We move around or move on from contract to contract. For many of us, this means formal job interviews. Many of those interviews include coding challenges; that can be a problem. Let’s talk about that!

Security Corner: Adventures in Hashing

By Eric Mann

Last month, the PHP community had the opportunity to come together for the excellent php[world] conference in Washington, D.C. As part of the event, we held a hackathon to work through some of the challenges posed by Cryptopals. Some of the cryptographic primitives we discussed were hashes, and it’s useful to take a more in-depth look at what they are and how to use them in PHP.

By James Titcumb

The final days of 2018 are looming on us, and I wanted to take a look back on some of the things, good and bad, that happened in and around the PHP community this year.

finally{}: The Seven Deadly Sins of Programming: Greed

By Eli White

As you are reading this magazine, we are in a time of winter holidays and typically associated with a spirit of goodwill towards others. That is embodied often as giving presents to people as a gesture of that goodwill. Unfortunately, this can have the opposite effect at times of causing greed to form as people want more and more given to them. Alternatively, they may misinterpret it as greed, as a specific green haired character learns in his holiday tale.

Dec 01 2016
Dec 01

dec16The twelfth issue of 2016 is now available! This month we look at how to write good tests with Behat and using Test Driven Development. This issue also includes articles on using HTTPlug to decouple your HTTP Client, Decoupled Blocks with Drupal and JavaScript. Our columnists have articles on writing a Chat bot, advice on securing your application’s secrets, making better bug reports, respecting diversity, and a look back at 2016.

Download your issue and read a FREE article today.

Oscar still remembers downloading an early version of the Apache HTTP server at the end of 1995, and promptly asking "Ok, what's this good for?" He started learning PHP in 2000 and hasn't stopped since. He's worked with Drupal, WordPress, Zend Framework, and bespoke PHP, to name a few. Follow him on Google+.
Jul 11 2016
Jul 11

Drupal's Batch API is great, it allows you to easily perform long running processes with feedback to the user.

But during Drupal 8's development processes it was one of the remaining systems that didn't get the full object oriented, service-based architecture.

Much of the batch API is largely unchanged from Drupal 7.

But that doesn't mean you can't write unit-testable callbacks.

Let's get started.

Our goal

Our goal here is to end up with a method that we can test with PHPUnit, without an installed Drupal and without the service container. So we're talking about a pure unit test. Not an integration or functional test. Or in Drupal 8 terms, a class that extends from UnitTestCase, not KernelTestBase or BrowserTestBase.

Our starting point

So lets start with a hypothetical example of the Drupal 7 norm for batch callbacks, a global function. This would typically live in a .module file or perhaps a .inc file. Loading this file would be performed by the Kernel during boostrap or the batch processing if we used the file key. We're going to use a hypothetical example of a batch callback that examines a product and if it is on sale but the sale date has passed, removes it from a fictional search index. The key point here is that we need some services from the container and we have some logic that is worth testing. Other that than, its purely fictional.

/**
 * Batch callback.
 */
function mymodule_batch_callback($product_id, &$context) {
  $repository = \Drupal::service('mymodule.product.repository');
  $promotion_search_index = \Drupal::service('mymodule.promotion_search_index');
  /** @var \Drupal\mymodule\ProductInterface $product */
  $product = $repository->find($product_id);
  if ($product->isOnSale()) {
    $end = $product->getSaleEndDateTime();
    $now = new \DateTime();
    if ($end->getTimestamp() < $now->getTimestamp()) {
      // Sale is finished.
      $promotion_search_index->delete($product);
    }
  }
}

Our first step towards refactoring is to move this into a static method on an object. That way we can use the auto-loader to take care of loading files. PHPunit can't find code hidden in .module files without the Kernel to bootstrap loading these files. Now its an object, the autoloader will load it for PHPUnit.

I like to call these objects batch workers, cause they do the work of the callback. So let's name our class ProductSaleExpiryBatchWorker. Our code now looks something like this and lives in mymodule/src/Batch/ProductSaleExpiryBatchWorker.php.

namespace Drupal\mymodule\Batch;
/**
 * Batch Worker to handle processing expired sale items.
 */
class ProductSaleExpiryBatchWorker {

  /**
   * Process the expiration for a given product.
   *
   * @param int $product_id
   *   Product ID to test.
   * @param array $context
   *   Batch context.
   */
  public static function process($product_id, &$context) {
    $repository = \Drupal::service('mymodule.product.repository');
    $promotion_search_index = \Drupal::service('mymodule.promotion_search_index');
    /** @var \Drupal\mymodule\ProductInterface $product */
    $product = $repository->find($product_id);
    if ($product->isOnSale()) {
      $end = $product->getSaleEndDateTime();
      $now = new \DateTime();
      if ($end->getTimestamp() < $now->getTimestamp()) {
        // Sale is finished.
        $promotion_search_index->delete($product);
      }
    }
  }

}

As you can see, all we've really done is move the code into an autoloaded class and into the static process method. We still can't unit test this code, because we need a bootstrapped container. This is because we call \Drupal::service(). We can do this because the Batch API uses call_user_func_array to execute the callback which works with static functions too.

Unfortunately, we can't use a service in our callback. Many places in Drupal core use the controller resolver service for their callbacks/executables which supports using serviceid:method notation. E.g you can do something like this for many form API attributes that support callbacks:

$output['comment_form'] = [
  '#lazy_builder' => ['comment.lazy_builders:renderForm', [
    $entity->getEntityTypeId(),
    $entity->id(),
    $field_name,
    $this->getFieldSetting('comment_type'),
  ]],
  '#create_placeholder' => TRUE,
];

So that uses the renderForm() method on the comment.lazy_builders service as its callback. But Batch API didn't get controller resolver integration. So we're stuck with things that can be passed to call_user_func_array(). But all is not lost. Enter the factory method.

The factory method

One of the key tennants of unit testability is dependency injection. And the most common method of dependency injection is constructor injection. So let take our static method, and instead of having it do the processing, let's make it a factory method.

Our code now looks like this:

namespace Drupal\mymodule\Batch;

use Drupal\mymodule\ProductRepositoryInterface;
use Drupal\mymodule\SearchIndexerInterface;

/**
 * Batch Worker to handle processing expired sale items.
 */
class ProductSaleExpiryBatchWorker {

  /**
   * The product ID we're processing.
   *
   * @var int
   */
  protected $productId;

  /**
   * @var \Drupal\mymodule\ProductRepositoryInterface
   */
  protected $repository;

  /**
   * @var \Drupal\mymodule\SearchIndexerInterface
   */
  protected $searchIndexer;

  /**
   * Constructs a new ProductSaleExpiryBatchWorker object.
   *
   * @param \Drupal\mymodule\ProductRepositoryInterface $repository
   *   Product repo.
   * @param \Drupal\mymodule\SearchIndexerInterface $search_indexer
   *   Search indexer.
   * @param int $product_id
   *   Product ID.
   */
  public function __construct(ProductRepositoryInterface $repository, SearchIndexerInterface $search_indexer, $product_id) {
    $this->productId = $product_id;
    $this->repository = $repository;
    $this->searchIndexer = $search_indexer;
  }

  /**
   * Process the expiration for a given product.
   *
   * @param int $product_id
   *   Product ID to test.
   * @param array $context
   *   Batch context.
   */
  public static function process($product_id, &$context) {
    $repository = \Drupal::service('mymodule.product.repository');
    $promotion_search_index = \Drupal::service('mymodule.promotion_search_index');
    $worker = new static($repository, $promotion_search_index, $product_id);
    $worker->dispatch($context);
  }

  /**
   * Process the expiration for a given product.
   *
   * @param int $product_id
   *   Product ID to test.
   * @param array $context
   *   Batch context.
   */
  protected function dispatch(&$context) {
    $product = $this->repository->find($this->productId);
    if ($product->isOnSale()) {
      $end = $product->getSaleEndDateTime();
      $now = new \DateTime();
      if ($end->getTimestamp() < $now->getTimestamp()) {
        // Sale is finished.
        $this->searchIndexer->delete($product);
      }
    }
  }

}

So we're changing the primary function of the static process method to be

  • Creating a new instance (factory method)
  • Calling the dispatch method

Now we have the bulk of our logic in the dispatch method.

And the dispatch method no longer relies on the container. It uses the injected repository and search indexer.

So now we can write our unit test case. We can mock products that are on sale, or have past end dates and we can wire up a mock repository to return them.

Wrapping up

So while we didn't get all the Object oriented advantages in core, it doesn't mean you can't write unit-testable batch callbacks.

If you're interested in working on modernizing the Batch API - as always - there's an issue for that.

Drupal 8 Batch API Unit testing Refactoring
May 01 2015
May 01

In this article we are going to look at automated testing in Drupal 8. More specifically, we are going to write a few integration tests for some of the business logic we wrote in the previous Sitepoint articles on Drupal 8 module development. You can find the latest version of that code in this repository along with the tests we write today.

Drupal 8 logo

But before doing that, we will talk a bit about what kinds of tests we can write in Drupal 8 and how they actually work.

Simpletest (Testing)

Simpletest is the Drupal specific testing framework. For Drupal 6 it was a contributed module but since Drupal 7 it has been part of the core package. Simpletest is now an integral part of Drupal core development, allowing for safe API modifications due to an extensive codebase test coverage.

Right off the bat I will mention the authoritative documentation page for Drupal testing with Simpletest. There you can find a hub of information related to how Simpletest works, how you can write tests for it, what API methods you can use, etc.

By default, the Simpletest module that comes with Drupal core is not enabled so we will have to do that ourselves if we want to run tests. It can be found on the Extend page named as Testing.

Once that is done, we can head to admin/config/development/testing and see all the tests currently available for the site. These include both core and contrib module tests. At the very bottom, there is also the Clean environment button that we can use if any of our tests quit unexpectedly and there are some remaining test tables in your database.

How does Simpletest work?

When we run a test written for Simpletest, the latter uses the existing codebase and instructions found in the test to create a separate Drupal environment in which the test can run. This means adding additional tables to the database (prefixed by simpletest_) and test data that are used to replicate the site instance.

Depending on the type of test we are running and what it contains, the nature of this replication can differ. In other words, the environment can have different data and core functionality depending on the test itself.

What kinds of tests are there in Drupal 8?

There are two main types of tests that we can write for Drupal 8: unit tests using PHPUnit (which is in core now) and functional tests (using Simpletest). However, the latter can also be split into two different kinds: web tests (which require web output) and kernel tests (which do not require web output). In this article we will practically cover only web tests because most of the functionality we wrote in the previous articles is manifested through output so that’s how we need to test it as well.

Writing any type of test starts by implementing a specific class and placing it inside the src/Tests folder of the module it tests. I also encourage you to read this documentation page that contains some more information on this topic as I do not want to duplicate it here.

Our tests

As I mentioned, in this article we will focus on providing test coverage for some of the business logic we created in the series on Drupal 8 module development. Although there is nothing complicated happening there, the demo module we built offers a good example for starting out our testing process as well. So let’s get started by first determining what we will test.

By looking at the demo module, we can delineate the following aspects we can test:

That’s pretty much it. The custom menu link we defined inside the demo.links.menu.yml could also be tested but that should already work out of the box so I prefer not to.

For the sake of brevity and the fact that we don’t have too much we need to test, I will include all of our testing methods into one single class. However, you should probably group yours into multiple classes depending on what they are actually responsible for.

Inside a file called DemoTest.php located in the src/Tests/ folder, we can start by adding the following:

<?php

namespace Drupal\demo\Tests;

use Drupal\simpletest\WebTestBase;

/**
 * Tests the Drupal 8 demo module functionality
 *
 * @group demo
 */
class DemoTest extends WebTestBase {

  /**
   * Modules to install.
   *
   * @var array
   */
  public static $modules = array('demo', 'node', 'block');

  /**
   * A simple user with 'access content' permission
   */
  private $user;

  /**
   * Perform any initial set up tasks that run before every test method
   */
  public function setUp() {
    parent::setUp();
    $this->user = $this->drupalCreateUser(array('access content'));
  }
}

Here we have a simple test class which for every test it runs, will enable the modules in the $modules property and create a new user stored inside the $user property (by virtue of running the setUp() method).

For our purposes, we need to enable the demo module because that is what we are testing, the block module because we have a custom block plugin we need to test and the node module because our logic uses the access content permission defined by this module. Additionally, the user is created just so we can make sure this permission is respected.

For the three bullet points we identified above, we will now create three test methods. Keep in mind that each needs to start with the prefix test in order for Simpletest to run them automatically.

Testing the page

We can start by testing the custom page callback:

/**
 * Tests that the 'demo/' path returns the right content
 */
public function testCustomPageExists() {
  $this->drupalLogin($this->user);

  $this->drupalGet('demo');
  $this->assertResponse(200);

  $demo_service = \Drupal::service('demo.demo_service');
  $this->assertText(sprintf('Hello %s!', $demo_service->getDemoValue()), 'Correct message is shown.');
}

And here is the code that does it.

First, we log in with the user we created in the setUp() method and then navigate to the demo path. Simpletest handles this navigation using its own internal browser. Next, we assert that the response of the last accessed page is 200. This validates that the page exists. However, this is not enough because we need to make sure the text rendered on the page is the one loaded from our service.

For this, we statically access the \Drupal class and load our service. Then we assert that the page outputs the hello message composed of the hardcoded string and the return value of the service’s getDemoValue() method. It’s probably a good idea to write a unit test for whatever logic happens inside the service but for our case this would be quite redundant.

And that’s it with the page related logic. We can go to the testing page on our site, find the newly created DemoTest and run it. If all is well, we should have all green and no fails.

drupal 8 automatated tests

Testing the form

For the form we have another method, albeit more meaty, that tests all the necessary logic:

/**
 * Tests the custom form
 */
public function testCustomFormWorks() {
  $this->drupalLogin($this->user);
  $this->drupalGet('demo/form');
  $this->assertResponse(200);

  $config = $this->config('demo.settings');
  $this->assertFieldByName('email', $config->get('demo.email_address'), 'The field was found with the correct value.');

  $this->drupalPostForm(NULL, array(
    'email' => '[email protected]'
  ), t('Save configuration'));
  $this->assertText('The configuration options have been saved.', 'The form was saved correctly.');

  $this->drupalGet('demo/form');
  $this->assertResponse(200);
  $this->assertFieldByName('email', '[email protected]', 'The field was found with the correct value.');

  $this->drupalPostForm('demo/form', array(
    'email' => '[email protected]'
  ), t('Save configuration'));
  $this->assertText('This is not a .com email address.', 'The form validation correctly failed.');

  $this->drupalGet('demo/form');
  $this->assertResponse(200);
  $this->assertNoFieldByName('email', '[email protected]', 'The field was found with the correct value.');
}

The first step is like before. We go to the form page and assert a successful response. Next, we want to test that the email form element exists and that its default value is the value found inside the default module configuration. For this we use the assertFieldByName() assertion.

Another aspect we need to test is that saving the form with a correct email address does what it is supposed to: save the email to configuration. So we use the drupalPostForm() method on the parent class to submit the form with a correct email and assert that a successful status message is printed on the page as a result. This proves that the form saved successfully but not necessarily that the new email was saved. So we redo the step we did earlier but this time assert that the default value of the email field is the new email address.

Finally, we need to also test that the form doesn’t submit with an incorrect email address. We do so again in two steps: test a form validation failure when submitting the form and that loading the form again will not have the incorrect email as the default value of the email field.

Testing the block

/**
 * Tests the functionality of the Demo block
 */
public function testDemoBlock() {
  $user = $this->drupalCreateUser(array('access content', 'administer blocks'));
  $this->drupalLogin($user);

  $block = array();
  $block['id'] = 'demo_block';
  $block['settings[label]'] = $this->randomMachineName(8);
  $block['theme'] = $this->config('system.theme')->get('default');
  $block['region'] = 'header';
  $edit = array(
    'settings[label]' => $block['settings[label]'],
    'id' => $block['id'],
    'region' => $block['region']
  );
  $this->drupalPostForm('admin/structure/block/add/' . $block['id'] . '/' . $block['theme'], $edit, t('Save block'));
  $this->assertText(t('The block configuration has been saved.'), 'Demo block created.');

  $this->drupalGet('');
  $this->assertText('Hello to no one', 'Default text is printed by the block.');

  $edit = array('settings[demo_block_settings]' => 'Test name');
  $this->drupalPostForm('admin/structure/block/manage/' . $block['id'], $edit, t('Save block'));
  $this->assertText(t('The block configuration has been saved.'), 'Demo block saved.');

  $this->drupalGet('');
  $this->assertText('Hello Test name!', 'Configured text is printed by the block.');
}

For this test we need another user that also has the permission to administer blocks. Then we create a new instance of our custom demo_block with no value inside the Who field and assert that a successful confirmation message is printed as a result. Next, we navigate to the front page and assert that our block shows up and displays the correct text: Hello to no one.

Lastly, we edit the block and specify a Test name inside the Who field and assert that saving the block configuration resulted in the presence of a successful confirmation message. And we close off by navigating back to the home page to assert that the block renders the correct text.

Conclusion

In this article, we’ve seen how simple it is to write some basic integration tests for our Drupal 8 business logic. It involves creating one or multiple class files which simply make use of a large collection of API methods and assertions to test the correct behavior of our code. I strongly recommend you give this a try and start testing your custom code early as possible in order to make it more stable and less prone to being broken later on when changes are made.

Additionally, don’t let yourself get discouraged by the slow process of writing tests. This is mostly only in the beginning until you are used to the APIs and you become as fluent as you are with the actual logic you are testing. I feel it’s important to also mention that this article presented a very high level overview of the testing ecosystem in Drupal 8 as well as kept the tests quite simple. I recommend a more in depth look into the topic going forward.

Oct 02 2012
Oct 02

Posted Oct 2, 2012 // 2 comments

While working on the SPS Module we became very well acquainted with Drupal's Unit Test Class. I will have a follow up Post on the lessons we learned about writing test, but here I will outline the basic steps for adding unit test to a module. It is worth noting that I will be talking about the Drupal Unit test not the Drupal Web Test.

Step 1 - Tell Drupal about your tests

Drupal will look for classes that define tests ,so all we need to do is make sure that those classes can be found. To do this we just add a files line to the .info file, so that Drupal knows which files to investigate for test classes.

MODULE.info

1
2
3
  name = MODULE
  core = 7.x
  files[] = tests/*.test

Now we can put all of our tests into the tests folder of the module, as long as we add .test to the file name.

Step 2 - Test Base Class

While a base class is not needed, it can be very helpful, as there are things (such as enabling your module) that can be done in a setup method.
tests/MODULEBase.test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
abstract class MODULEBaseUnitTest extends DrupalUnitTestCase {
  /**
   * One using of this function is to enable the module used for testing, any dependencies
   * or anything else that might be universal for all tests
   */
  public function setUp() {
    parent::setUp();
    //enable module
    $this->enableModule('MODULE');
 
    // enable dep and other thing for all tests
  }
 
  /**
   * Fake enables a module for the purpose of a unit test
   *
   * @param $name
   *  The module's machine name (i.e. ctools not Chaos Tools)
   */
  protected function enableModule($name) {
    $modules = module_list();
    $modules[$name] = $name;
    module_list(TRUE, FALSE, FALSE, $modules);
  }
  ...
}

In the code sample, we simply create a base class that extends DrupalUnitTestCase, and add a setup method, to take care of anything that needs to be done before all of our tests. We also include an enableModule method that fakes enabling a module (the DrupalUnitTestCase, does not have access to a database, so enabling a module the normal way is not available).

Other things we can add to the base case are new methods to support our tests (such as asserts) that one might want to use over and over. For example in SPS module we add an assertThrows, which tests to ensure the correct exception is thrown (note this assert only works in PHP 5.3)
tests/MODULEBase.test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
abstract class MODULEBaseUnitTest extends DrupalUnitTestCase {
  ...
  /**
   * One can also add helper assert functions that might get used in tests
   *
   * This one test if the correct Exceptions is thrown (5.3 only)
   */
  protected function assertThrows(Closure $closure, $type, $error_message = NULL, $message) {
    try {
      $closure();
    }
    catch (Exception $e) {
      if (!($e instanceof $type)) {
        throw $e;
      }
      if (isset($error_message)) {
        if ($e->getMessage() != $error_message) {
          $this->fail($message, "SPS");
          return;
        }
      }
      $this->pass($message, "SPS");
      return;
    }
    $this->fail($message, "SPS");
  }
 
  /**
   * One can also add helper assert functions that might get used in tests
   *
   * Test that an object is an instance of a class
   *
   * @param $class
   * @param $object
   * @param $message
   */
  protected function assertIsInstance($class, $object, $message) {
    if ($object instanceof $class) {
      $this->pass($message, "SPS");
    }
    else {
      $this->fail($message, "SPS");
    }
  }
}

After we have set up our base class, now we can start making our tests.

Step 3 - Write Tests

OK, now we get to write tests! The structure here gets a little bit confusing, one can have as many test classes as one wants, and each test class can have as many test methods, and each test method can have many assertions.

In the SPS module we did a test class for each class provided by the SPS module, with a test method for each method provided by the class and then a test for each method. We found this to be an effective way to structure the test, but there is no required structure.

I also used one file for each test class, each of which extended the base class defined earlier. Each test class should define a getInfo method, to tell us about the test.

The getInfo method returns a array with keys of name, description, and group (I use the module name for this).

1
2
3
4
5
6
7
8
9
10
class MODULETestnameUnitTest extends MODULEBaseUnitTest {
  static function getInfo() {
    return array(
      'name' => 'MODULE Testname ',
      'description' => 'Test the public interface to the Testname of the MODULE module',
      'group' => 'MODULE',
    );
  }
  ...
}

Now for the test methods. Each method that starts with the word 'test' will be run as a test. Each test should include one or more asserts. One can look at the Drupal Unit Test methods to see all of the asserts. AssertTrue and AssertEqual will be the most used asserts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MODULETestnameUnitTest extends MODULEBaseUnitTest {
  ...
  /**
   * All test start with test in lowercase letters
   *
   * One can use any of the asserts in this space
   */
  public function testContruction() {
    $value = $this->getCondition('baseball');
    $this->assertIsInstance(
      'MODULECondition',
      $value,
      'Description of what is being checked in this assertion'
 
    $expect = new ModuleCondition('baseball', 'other');
    $this->assertEqual(
      $value,
      $expect,
      'Description of what is being checked in this assertion'
    );
  }
  ...
}

Other methods (those that do not start with 'test') can be used in the test for doing tasks that might be needed by multiple test methods.

1
2
3
4
5
6
7
8
9
10
11
12
class MODULETestnameUnitTest extends MODULEBaseUnitTest {
  ...
  /**
   * Methods that do not start with test, are ignored as test, but can be used for function
   * that might be reused on multiple test.
   */
  protected function getCondition($name) {
 
    return MODULE_get_condition($name, TRUE);
  }
 
}

Running Tests

The last item is to run these tests; that can be done by using the /scripts/run-tests.sh script (this is off of the drupal root). One must have the simpletest module enable to run these tests.

To run only the tests in a specific group (remember I set it to the name of the module earlier) pass the group name as the first argument to the run-tests.sh script.

 php scripts/run-tests.sh GROUPNAME
While one is developing one's tests, or if obe has to drill down on a failing test, one can use the verbose and class flags. This will give a more verbose report on only one class' tests.
 php scripts/run-tests.sh --verbose --class CLASSNAME

One can also use the Testing admin area for running the test (they are grouped by group name), but I find that the script is much more conducive if one is doing Test Driven Development.

When Erik Summerfield joined our software development team, we knew that his natural talents in math and economics would be an asset to our team and clients alike. Plus, his experiences in various programming languages including .NET/C#, perl, ...

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web