Book Image

Instant Hands-on Testing with PHPUnit How-to

By : Michael Lively
Book Image

Instant Hands-on Testing with PHPUnit How-to

By: Michael Lively

Overview of this book

No developer wants to accept the inherent difficulty of writing software as an excuse for not finding the bugs in our code before anyone else does. PHPUnit is a framework that was created to allow developers to solve that very problem. It provides a feature-rich environment with most of the tools necessary to provide adequate tests for any project. "Instant Hands-on Testing with PHPUnit How-to" provides a thorough overview of the functionality provided by the PHPUnit framework. It shows how the plethora of features in the framework can be used to write tests for real world projects to ensure they function and will continue to function in the ways that you expect. This book will show how you can set up the scaffolding necessary to run unit tests in your project with PHPUnit. It will walk you through the process of how to write a basic test and how to maintain your project's test suite. You will learn how to use some of the more advanced features of PHPUnit and then see how you can use mock objects to isolate the code you are testing. We will then discover how to create tests that verify your interaction with databases and even see how you can use PHPUnit to understand which code you are actually testing. At the end of the book you will have all of the basic understanding necessary to begin adding tests to your project. This book provides a great foundation for becoming a expert at writing unit tests.
Table of Contents (7 chapters)

Using data providers (Intermediate)


Data providers are a great way to test many different variants of a single method call quickly. When you have a method that is responsible for applying an algorithm to the method arguments and come up with a predictable result then data providers are a great option.

How to do it...

Modify the contents of test/CardTest.php to the following:

<?php
class CardTest extends PHPUnit_Framework_TestCase
{
  private $card;
  public function setUp()
  {
    $this->card = new Card('4', 'spades');
  }
  public function testGetNumber()
  {
    $actualNumber = $this->card->getNumber();
    $this->assertEquals(4, $actualNumber, 'Number should be <4>');
  }
  public function testGetSuit()
  {
    $actualSuit = $this->card->getSuit();
    $this->assertEquals('spades', $actualSuit, 'Suit should be <spades>');
  }
  public function matchingCardDataProvider()
  {
    return array(
      array(new Card('4', 'hearts'), true, 'should match'),
      array(new Card('5', 'hearts'), false, 'should not match')
    );
  }

  /**
   * @dataProvider matchingCardDataProvider
   */
  public function testIsInMatchingSet(Card $matchingCard, $expected, $msg)
  {
    $this->assertEquals($expected, $this->card->isInMatchingSet($matchingCard),
        "<{$this->card->getNumber()} of {$this->card->getSuit()}> {$msg} "
        . "<{$matchingCard->getNumber()} of {$matchingCard->getSuit()}>");
  }
}

How it works...

The new matchingCardDataProvider() method is our data provider. It should return an array containing multiple arrays of arguments to pass into a test method. The method does need to be public as it actually gets called from outside the test case. Also, the method does not have to be static, as you do not have reliable access to any variable you should treat the method as though it were static.

You then need to assign the data provider to one of your test methods. This is done using the @dataProvider annotation. In this example, the annotation is assigned to the testIsInMatchingSet() method. You will notice that this method has three parameters. This is exactly the same number of items there are in each sub-array returned by matchingCardDataProvider().

The three parameters in this example are the arguments provided for isInMatchingSet(), an expected value, as well as part of the assertion failure message. When using data providers you can use the Don't Repeat Yourself (DRY) principal very effectively to reduce the amount of code you have to write for each test. However, this does need to be balanced with readability. If you reduce the amount of code that has to be written, but someone else can't understand what the test is doing then the effectiveness and maintainability of the test is actually reduced.

Identifying test failures

You may be wondering how to identify which data set failed while using the data providers. Fortunately, PHPUnit takes care of this for you. Modify the matchingCardDataProvider() method to return a row that will force the test to fail.

public function matchingCardDataProvider()
{
  return array(
    array(new Card('4', 'hearts'), true, 'should match'),
    array(new Card('5', 'hearts'), false, 'should not match'),
    array(new Card('4', 'clubs'), false, 'should not match')
  );
}

Then, run the unit test suite and you will see the following:

As you can see it tells you the index of the data set along with the actual parameters passed as a part of that data set.

This can be improved even further by providing keys to the array that your data provider returns. Try using the following data provider:

public function matchingCardDataProvider()
{
  return array(
    '4 of Hearts' => array(new Card('4', 'hearts'), true, 'should match'),
    '5 of Hearts' => array(new Card('5', 'hearts'), false, 'should not match'),
    '4 of Clubs' => array(new Card('4', 'clubs'), false, 'should not match')
  );
}

Run the tests again to see the following output:

As you can see, you can utilize data providers to not only consolidate your code, but you can also make it very easy to isolate the data set you have problems with.