PHPUnit style dataProvider in Python unit test

PHPUnit has a handy feature with which you can provide testdata to your tests. This is called a data provider, and is implemented by annotating a test with @dataProvider methodName. Python’s unittest module doesn’t seem to have such a feature.

PHPUnit’s version

The data provider returns a two-dimensional array of test arguments. For example:

class CssParserTest extends PHPUnit_Framework_TestCase {
    function setUp() {
        $this->parser = new CssParser();
    }
 
    /**
     * @dataProvider cssColors
     */
    function testParseColor($color, $notation) {
        $this->assertEquals($color, $this->parser->parseColor($notation));
    }
 
 
    function cssColors() {
        return array(
            array(array(0, 0, 0), '#000'),
            array(array(0, 0, 0), '#000000'),
            array(array(0, 0, 0), 'rgb(0, 0, 0)')
            array(array(0, 0, 0), 'black')
        );
    }
}

Running this test would call the testParseColor() test 4 times, with each of the arrays returned by cssColors() as the arguments.

Python: providing test data using a decorator

While writing tests for some Python code, I discovered that Python’s unittest doesn’t seem to have such a feature. So I implemented my own, using a decorator:

def data_provider(fn_data_provider):
    """Data provider decorator, allows another callable to provide the data for the test"""
    def test_decorator(fn):
        def repl(self, *args):
            for i in fn_data_provider():
                try:
                    fn(self, *i)
                except AssertionError:
                    print "Assertion error caught with data set ", i
                    raise
        return repl
    return test_decorator

Example usage:

class CssParserTest:
    def setUp(self):
        self.parser = CssColor()
 
    colors = lambda: (
        ( (0, 0, 0), '#000' ),
        ( (0, 0, 0), '#000000' ),
        ( (0, 0, 0), 'rgb(0, 0, 0)' ),
        ( (0, 0, 0), 'black' )
    )
 
    @data_provider(colors):
    def test_parse_color(self, color, notation):
        self.assertEquals(color, self.parser.parse_color(notation))

Suggestions of alternatives are greatly appreciated, by the way 🙂

This entry was posted in Development and tagged , . Bookmark the permalink. Trackbacks are closed, but you can post a comment.

7 Comments

  1. Posted May 15, 2011 at 14:11 | Permalink

    I just had a super weird-out moment, I am writing the exact same function, and wanted to test it the exact same way! Perfect, thanks, just what I needed!

  2. Posted June 8, 2011 at 11:48 | Permalink

    Nice work – I don’t quite understand how python devs write unittests without this.

    I’ve ported a version of yours into an ecommerce framework I’m writing (https://github.com/tangentlabs/django-oscar). I’ve attributed your original in the docblock.

    https://github.com/tangentlabs/django-oscar/blob/master/oscar/test/decorators.py

  3. drm
    Posted June 8, 2011 at 13:48 | Permalink

    Nice 🙂 Good to see it in action.

  4. Posted September 13, 2012 at 12:38 | Permalink

    Thanks !!

    I use it so much I packaged it: http://pypi.python.org/pypi/unittest-data-provider/ (and it’s on github: https://github.com/yourlabs/unittest-data-provider )

    Hope this helps you too

    Thanks again

  5. Rafael Capucho
    Posted September 18, 2012 at 23:13 | Permalink

    Thank you very much for your contribution, very clever!

  6. Posted November 26, 2012 at 18:18 | Permalink

    I packaged it: http://pypi.python.org/pypi/unittest-data-provider/

    Repo: https://github.com/yourlabs/unittest-data-provider

    I’d be glad to hand it over to you if you want. Anyway, I think that the credits are proper if I can do anything please let me know.

  7. drm
    Posted January 9, 2013 at 18:05 | Permalink

    Awesome, James, thanks for your work. Your comments were hanging in the twilight “pending” zone and they escaped my attention in my cluttered mailbox 😉

Post a Comment

Your email is never published nor shared.

You may use these HTML tags and attributes <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Subscribe without commenting