Mimicking Data Provider Functionality In Drupal SimpleTest

10th July 2012

Although Drupal SimpleTest is an extremeley useful module it doesn't currently support data providers, which is a shame as I use that feature quite a bit in other testing frameworks. A data provider is a mechanism that allows you to call a single test case multiple times with different arguments so that you can ensure the correct output each time. This is useful because testing a single function once is fine, but testing it with a variety of different values can otherwise mean having multiple test cases.

To mimic this functionality in Drupal SimpleTest you can create a data provider method that returns an array, which is then used to test a particular function.

For example, let's say I have the following (trivial) function in a Drupal module.

  1. function my_module_add_numbers($number1, $number2) {
  2. return $number1 + $number2;
  3. }

I would normally test this in Drupal SimpleTest by creating a method in testing class and feed some parameters into the function. Here is the test method with the rest of the test class removed.

  1. public function testAdding1And1Equals3() {
  2. $this->assertEqual(my_module_add_numbers(1, 2), 3);
  3. }

Obviously this function just adds two numbers together, but what happens if the parameters are different? What if we pass two strings instead of integers? The function obviously doesn't degrade nicely if this happens and so we need to add some error checking to the function to make sure it can handle different forms of input. In order to unit test our work properly we need to pass multiple arguments to this function using a data provider. Here is the data provider we will use for this unit test. The first two values in each array are the parameters we will use, and the third is the expected output.

  1. public function addingNumbersDataProvider() {
  2. return array(
  3. array(1, 2, 3),
  4. array('1', '2', 3),
  5. array('monkey', 'wrench', 0)
  6. );
  7. }

The only change we make to the unit test is to loop through the data from the data provider, feed the parameters into the function and test the correct output is returned.

  1. public function testAddingNumbers() {
  2. foreach ($this->addingNumbersDataProvider() as $data) {
  3. $this->assertEqual(my_module_add_numbers($data[0], $data[1]), $data[2]);
  4. }
  5. }

With unit test in hand we can see that our tests now fail. So we can ensure the correct output with some simple changes to the original function.

  1. function my_module_add_numbers($number1, $number2) {
  2. if (!is_numeric($number1) && !is_numeric($number2)) {
  3. return 0;
  4. }
  5. return (int)$number1 + (int)$number2;
  6. }

Using this method means that you can now call a single test case multiple times without having to have multiple test cases. We can also ensure that our function works no matter what sort of data we throw at it. This is especially useful for things like escaping strings as you will want to make sure that any security threats are dealt with safely. If any new security threat happens to crop up you can always add this to your data provider and ensure that your functions are able to deal with it.

One small downside to this approach is that you will almost certainly get lost if you have lots of elements in your data provider array. One way to get around this is to extract the variables in the array via a list() function. In the following example we take a data array consisting of 4 elements and create variables from these elements so we can better understand what is going on in the test.

  1. public function calculationDataProvider() {
  2. return array(
  3. array(2, 2, 2, 4),
  4. array(1, 1, 1, 2)
  5. );
  6. }
  7.  
  8. public function testCalculation() {
  9. foreach ($this->calculationDataProvider() as $data) {
  10. list($height, $length, $width, $total_devices_count) = $data;
  11. // run calculation
  12. $result = theCalculation($height, $length, $width);
  13. // assertions
  14. $this->assertEqual($result['total_devices_count'], $total_devices_count);
  15. }
  16. }

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.