Problem with PHPUnit and Data Providers
Asked Answered
Z

6

26

I have the following test case:

include_once('../Logger.php');

class LoggerTest extends PHPUnit_Framework_TestCase {

    public function providerLogger() {
        return new Logger;
    }

    /**
     * @dataProvider providerLogger
     */
    public function testAddStream($logger) {
        $this->assertTrue(false);
    }

}

When I run it in PHPUnit, I get:

PHPUnit 3.4.14 by Sebastian Bergmann.

..........

Time: 0 seconds, Memory: 5.75Mb

OK (1 tests, 0 assertions)

Test should fail, but it doesn't. I tried having:

public function providerLogger() {
    return array(new Logger);
}

But I get:

The data provider specified for LoggerTest::testAddStream is invalid.

I tried declaring it static (like the manual says), but still no difference.

I remember having it working in a similar fashion before, but I could be wrong. What am I missing?

Thanks in advance for your help.

PHPUnit 3.4.14 (taken from PEAR) on PHP 5.3.3

Zouave answered 24/11, 2010 at 0:2 Comment(4)
1 tests, 0 assertions. Your test was not even called. I bet the issue is not in the data provider.Corby
Fact is, if I remove the @dataProvider thing, I get an error because testAddStream() requires a parameter. If I remove the parameter completely, test fails as it should.Zouave
I am having an issue using PHPUnit in Zend Studio where if I register a namespace with Zend_Autoloader, using @dataProvider causes it to (attempt to) load the name of the test as a class. I'll lay odds that these 2 problems are related.El
Post your code as a question and we'll find out! ;)Zouave
N
33

Minor update: It's OK to use instance methods as provider since version 3.2 (or somewhere around that). Have a look at the comments


The provider must look like this.

public static function providerLogger() {
    return array(
      array(new Logger)
    );
}

First of all: The method must be static if you are using phpunit version lower than 3.3 .

The array s are important. Its not that hard to understand. The outer array has one value for each iteration the test should get called. Here the test just get called once. The inner arrays are the parameters (in order) the test is invoked with. Your test expects exactly one parameter, so the inner arrays always needs exactly one value. Another little example

public static function addTestProvider () {
    return array(
        /* First + Second = third? */
        array(1,4,5),
        array(3,3,6),
        array(5,5,6)
    );
}
public function testAdd ($a, $b, $result) {
    $this->assertEquals($result, $a + $b);
}

Here testAdd gets executed 3 times, one for every second-level array, and it will receive the values from the inner array s. You may notice, that the test will fail and provides you a message in which iteration of the dataset (here #3, because 5+5 is not 6 ;)) the assertion failed.

Nicki answered 24/11, 2010 at 1:45 Comment(8)
That was it it seems. I had tried static, I had tried arrays, I also had tried arrays of arrays (the actual solution), but I must have done something wrong, because it works now. Thanks a bunch!Zouave
am I missing something? Why does it have to be static? The PHPUnit manual uses regular methods tooDoxia
Hmm ... You're right. So maybe its ok, if its not static ;) It seems that changed with 3.3. At least the example in 3.2 is different ;)Nicki
Thanks for the example but I don't understand why you insist the method must be static ?!Yawp
@Hatef Please read the comments. That changed somewhen and now its OK to use instance members as providers too.Nicki
@Nicki Thank you! In PHPUnit 4.3 there was no such condition. In PHPUnit 6 it is present. The problem occurs because for not-static function PHPUnit tries to create an object for your TestCase class and sometimes it fails if you need to do it on yourself.Buncombe
We've come full circle. As of PHPUnit v10, data providers must be static - docs.phpunit.de/en/10.0/…Malcommalcontent
@BradKent PHPUnit v10 will trigger a deprecation warning for non-static data providers but they should still work ... for now.Piercy
P
6

I had the same problem, and it was solved, when i deleted the empty constructor,that was auto generated. Iam not sure why this solves the problem. I also had no test method named like the class. The provider method doesnt need to be static, so far my test run without static. But also run when i make the provider method static

Prevaricate answered 25/8, 2011 at 10:3 Comment(2)
Thank you so much! Deleting __construct really helps.Corycorybant
I also found that you can use __construct, but don't forget to call parent::_construct like this: public function __construct($name = NULL, array $data = array(), $dataName = '') { //... parent::__construct($name, $data, $dataName); }Corycorybant
K
1
<?php

require_once 'calculator.php';

/**
 * Calculator test case.
 */
class CalculatorTest extends PHPUnit_Framework_TestCase {

    /**
     * @var Calculator
     */
    private $Calculator;

    /**
     * Prepares the environment before running a test.
     */
    protected function setUp() {
        parent::setUp ();
        // TODO Auto-generated CalculatorTest::setUp()
        $this->Calculator = new Calculator(/* parameters */);
    }

    /**
     * Cleans up the environment after running a test.
     */
    protected function tearDown() {
        // TODO Auto-generated CalculatorTest::tearDown()
        $this->Calculator = null;
        parent::tearDown ();
    }

    /**
     * Constructs the test case.
     */
    public function __construct() {
        // TODO Auto-generated constructor
    }

    /**
     * Tests Calculator->add()
     *
         * @dataProvider provider
         */
    public function testAdd($a, $b, $c) {
        // TODO Auto-generated CalculatorTest->testAdd()
        //$this->markTestIncomplete ( "add test not implemented" );

        //$this->Calculator->add(/* parameters */);
        $this->assertEquals($this->Calculator->add($a, $b), $c);
    }

    public static function provider()
    {
        return array(
          array(1, 1, 1),
          array(1, 1, -1),
          array(4, 2, 2),
          array(1, 1, 1)
        );
    }
}

is the complete set of code

Killough answered 15/9, 2011 at 13:9 Comment(0)
E
0

I have also found that you cannot directly chain data providers:

class ProviderTest extends PHPUnit_Framework_TestCase {

    public function provider() {
        return array(array('test'));
    }

    /**
     * @dataProvider provider
     */
    public function providerTest1($test) {
        $this->assertTrue($test);
        return array(array($test));
    }

    /**
     * @dataProvider providerTest1
     */
    public function providerTest2($test) {
        $this->assertEquals('test', $test);
    }

}

Apparently, PHPUnit calls all the provider functions before running any tests, so you can't even use separate provider functions to feed test result data to other tests. The best you can do is to simulate:

class ProviderTest extends PHPUnit_Framework_TestCase {

    private $provider_data = array();

    public function provider() {
        return array(array('test'));
    }

    /**
     * @dataProvider provider
     */
    public function testProvider1($test) {
        $this->assertFalse(empty($test));
        array_push($this->provider_data, array($test));
    }

    /**
     * @depends testProvider1
     */
    public function testProvider2($test = NULL) {
        if(is_null($test)) {
            // simulate a provider
            foreach($this->provider_data as $row) {
                call_user_func_array(array($this, __METHOD__), $row);
            }
        } else {
            $this->assertEquals('test', $test);
        }
    }

}
Enchilada answered 14/10, 2011 at 4:47 Comment(0)
W
-2

Remove the parameter from public function testAddStream($logger) and try again. I don't believe PHPUnit will invoke a test which requires parameters it is unable to pass.

Wrier answered 24/11, 2010 at 0:19 Comment(6)
If I remove the parameter, test gets executed and fails as it should. The @dataProvider block purpose is to actually pass parameters to a test, like demonstrated in the manual.Zouave
Ah, I see. Your dataprovider function needs to be public and static. See this blurb in the manual : "A data provider method must be public and static and either return an array of arrays or an object that implements the Iterator interface and yields an array for each iteration step. For each array that is part of the collection the test method will be called with the contents of the array as its arguments."Wrier
@Macy: Unfortunately, I tried that too (static, non-static, return array, return object...) and still the same result...Zouave
Can you show me the version of your code where you tried a public static function returning a single element array? Have you also tried making that single element array return just an integer instead of a new Logger object? It could be the case that PHPUnit is eating a runtime error upon Logger object instantiation.Wrier
Whats about my solution below. It stands there for about 20min ;)Nicki
@Macy: KingCrunch has it. Thanks for your help too!Zouave
D
-2

Behold I have achieved a pattern to achieve test dependencies for dataProviders! In this way you can chain dataProviders.

class ProviderDependencyTest extends PHPUnit_Framework_TestCase
{
    private static $dataSet;

    public function provideData()
    {
        self::$dataSet = array(
                    array(2,2,4),
                    array(1,0,2),
                    array(0,0,0)
                );

        //use static storage so you don't have to call the dataProvider again
        return self::$dataSet;
    }

    public function testProvideAdd()
    {
        $data = self::$dataSet;

        $this->assertEquals(3,count($data[0]));

        return $data[0];
    }

    /**
     * @depends testProvideAdd
     */
    public function testAdd($data)
    {
        $sum = $data[0] + $data[1];

        $this->assertEquals($data[2], $sum);

        return array($sum,$data[0],$data[1]);
    }

    /**
     * @depends testAdd
     */
    public function testSubtract($data)
    {
        $difference = $data[0] - $data[1];

        $this->assertEquals($data[2], $difference);

        return array($difference,$data[0],$data[1]);
    }

    /**
     * @depends testSubtract
     */
    public function testMultiply($data)
    {
        $product = $data[0] * $data[2];

        $this->assertEquals($data[1], $product);

        return $product;
    }

    /**
     * @depends testMultiply
     *
     * @dataProvider provideData
     */
    public function testMath($a,$b,$c)
    {
        //don't redo the first set of tests
        if(array($a,$b,$c) == self::$dataSet[0])
        {
            return;
        }

        $sum = $this->testAdd(array($a,$b,$c));
        $difference= $this->testSubtract($sum);
        $product = $this->testMultiply($difference);

        $this->assertInternalType('integer', $product);
    }
}

The 2nd data set fails 1 test to illustrate.

Dovetailed answered 16/2, 2013 at 4:32 Comment(2)
Please do not include your website link in your answers in this manner. The link is not directly related to the question, so its appearance here looks like spam. I am removing it for you.Drier
sure thing! just trying to helpDovetailed

© 2022 - 2024 — McMap. All rights reserved.