25

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

netcoder
  • 66,435
  • 19
  • 125
  • 142
  • 1 tests, **0** assertions. Your test was not even called. I bet the issue is not in the data provider. – zerkms Nov 24 '10 at 00:16
  • 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. – netcoder Nov 24 '10 at 00:27
  • 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. – Duncan Nov 24 '10 at 02:50
  • 1
    Post your code as a question and we'll find out! ;) – netcoder Nov 24 '10 at 02:54

6 Answers6

32

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.

rineez
  • 753
  • 13
  • 33
KingCrunch
  • 128,817
  • 21
  • 151
  • 173
  • 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! – netcoder Nov 24 '10 at 02:10
  • 15
    am I missing something? Why does it have to be static? The [PHPUnit manual uses regular methods too](http://www.phpunit.de/manual/3.4/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers) – Gordon Nov 27 '10 at 22:19
  • 1
    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 ;) – KingCrunch Nov 28 '10 at 18:50
  • Thanks for the example but I don't understand why you insist the method must be static ?! – hatef Jul 29 '15 at 10:33
  • @Hatef Please read the comments. That changed somewhen and now its OK to use instance members as providers too. – KingCrunch Aug 02 '15 at 21:06
  • @KingCrunch 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. – Tomasz Kuter Jan 31 '18 at 15:27
  • 1
    We've come full circle. As of PHPUnit v10, data providers must be static - https://docs.phpunit.de/en/10.0/writing-tests-for-phpunit.html#data-providers – Brad Kent Mar 14 '23 at 14:57
  • @BradKent PHPUnit v10 will trigger a deprecation warning for non-static data providers but they should still work ... for now. – apokryfos Jul 05 '23 at 07:30
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

Christian Steinmann
  • 1,156
  • 1
  • 11
  • 13
  • 1
    Thank you so much! Deleting __construct really helps. – barbushin Sep 22 '11 at 09:18
  • 8
    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); } – barbushin Sep 22 '11 at 09:32
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

LPL
  • 16,827
  • 6
  • 51
  • 95
Karkotagan
  • 21
  • 1
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);
        }
    }

}
Jonathon Hill
  • 3,445
  • 1
  • 33
  • 31
-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.

Andrew Barber
  • 39,603
  • 20
  • 94
  • 123
Josh Woodcock
  • 2,683
  • 1
  • 22
  • 29
-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.

Macy Abbey
  • 3,877
  • 1
  • 20
  • 30
  • 1
    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](http://www.phpunit.de/manual/3.2/en/writing-tests-for-phpunit.html). – netcoder Nov 24 '10 at 00:28
  • 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." – Macy Abbey Nov 24 '10 at 00:32
  • @Macy: Unfortunately, I tried that too (static, non-static, return array, return object...) and still the same result... – netcoder Nov 24 '10 at 01:42
  • 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. – Macy Abbey Nov 24 '10 at 01:44
  • Whats about my solution below. It stands there for about 20min ;) – KingCrunch Nov 24 '10 at 02:09
  • @Macy: KingCrunch has it. Thanks for your help too! – netcoder Nov 24 '10 at 02:11