Data Providers

Posted 2012-08-15
Written by Matt Frost
Category code

I have to admit, when I did my first unit test in a pair programming session over Skype with a grumpy Canadian guy; the thought of using data providers was a bit foreign to me. My idea of testing was always using print_r, var_dump, throwing exits around or manipulating the display_errors setting; so when I started writing unit tests I'd provide multiple data sets in a for loop and call an assertion on each pass. I decided to look into data providers, because that felt a little bit dirty to me; I can't really explain it but it didn't feel right. Rather than blathering on about how I learned how to use data providers in PHPUnit, I'm going to provide an example similar to the example that helped me grasp it.

First of all, I use PHPUnit and have become accustomed to using the doc blocks to identify my tests. So generally speaking, I will not have the word 'test' in my test methods - just getting that out of the way. The example I'm going to use is very simple and method that we're going to test is going to be simple as well. Since we're writing a unit test, we want to make sure we're testing individual units in isolation. We have an application that determines if the provided zip code is within a range that we've predetermined to be acceptable. Let's do something stupid simple.

class AddressVerify
{
    private $validZipCodes = array('12345','12346','12347','12348');

public function checkForValidZipCode($zip)
{
    if(in_array($zip,$this->validZipCodes)){
        return true;
    }
    return false;
}

}

There's a simple class with a simple method. Writing this test should be pretty easy, but to be thorough let's assume that we want to run a whole bunch of zip codes through here. We don't know if the requirements are ever going to change for this; so we want to get a test in place as a starting point.

ValidZipCodeTest extends PHPUnit_Framework_TestCase
{

Since I've predetermined that I'm going to need a data provider, I need to determine what I'm going to provide. Since I know the zip codes I'm validating; I'm going to provide a zip code and the expected result. These are really simple to set up, although the larger they are the more monotonous they tend to be. It's a method that returns an array.

public function provideZipCodes()
{
    return array(
        array('12345',true),
        array('22345',false),
        array(32345',false),
        array('12346',true)
    );
}

There's our simple data provider, we have 2 zip codes we expect to pass and 2 zip codes we expect to fail. To write the test to use this data provider is very simple.

/**
* @test
* Given that we're provided with a zip code
* the checkForValidZipCode method should return true if the zip code is in 
* the accepted list and false otherwise
* @dataProvider provideZipCodes
**/
public function ProvidedZipCodesAreCorrectlyVerified($zip,$expectedResult)
{
    $verifier = new AddressVerify();
    $this->assertEquals($verifier->checkForValidZipCode($zip),$expectedResult);
}

By providing the @dataProvider provideZipCodes line, we're telling PHPUnit to run this test for each array in the data provider method. Also notice, our test has parameters; we've named those according to the pieces of data we're providing. In our scenario, this test will be run 4 times provided our method returns what we're expecting it to return. In the event that the method doesn't return what we're expecting, PHPUnit will tell us what set of data caused our assertion to fail.

That's really all there is to it, if you find yourself writing multiple tests or loops within your actual tests; you may need to use a data provider.

--Ed note: Still looking for a decent way to style code, so if you have suggestions let me know

Comments

There are no comments

Posting comments after has been disabled.