42

One question in short: can phpunit use multiple data provider when running test?

For example, I have a method called getById, and I need to run both successful and unsuccessful testcases for it.

The successful testcases means that it can return a corresponding record. And for the unsuccessful, the input can fall in two categories: invalid and failed.

The invalid means that the input is not legal, while failed means the input could be valid, but there is no corresponding record with that ID.

So the code goes like this:

/** 
 * @dataProvider provideInvalidId
 * @dataProvider provideFailedId
 */
public function testGetByIdUnsuccess($id)
{   
    $this->assertNull($this->model->getById($id));
}   

But it turned out that only the first data provider has been used, ignoring the second one. Though I am not sure this senario is common or not, but here is the question. Can we use multiple data provider? And if we can, how?

PS: did not find too much help in here

imcoddy
  • 803
  • 2
  • 8
  • 10
  • I am in a similar situation where having separate dataProviders would make the code more readable (sort of documenting the code and test cases through the data provided to them). I wonder what would be the best practice? – Pitt Apr 13 '16 at 04:31

5 Answers5

35

Just an update to the question, a pull request was accepted and now the code:

/** 
 * @dataProvider provideInvalidId
 * @dataProvider provideFailedId
 */
public function testGetByIdUnsuccess($id)
{   
    $this->assertNull($this->model->getById($id));
}

Will work on PHPUnit 5.7, you'll be able to add as many providers as you want.

Alexandre Borela
  • 1,576
  • 13
  • 21
  • 2
    One gotcha: I had very generic test case names. If you give names you have to make sure that they are unique over your providers or only the last test case with that name will be executed. – Adam P. Feb 19 '21 at 07:53
15

You can use a helper function as shown below. The only problem is if the total number of test cases provided by all "sub data providers" is large, it can be tedious to figure out which test case is causing a problem.

/** 
 * @dataProvider allIds
 */
public function testGetByIdUnsuccess($id)
{   
    $this->assertNull($this->model->getById($id));
}  

public function allIds()
{
    return array_merge(provideInvalidId(),provideFailedId());
}
Dwayne Towell
  • 8,154
  • 4
  • 36
  • 49
6

You can also use CrossDataProviders which allows you to use a combination of data providers with each other

<?php

/** 
 * @dataProvider provideInvalidIdAndValues
 */
public function testGetByIdUnsuccess($id, $value)
{   
    $this->assertNull($this->model->getById($id));
}   

function provideInvalidIdAndValues() {
    return DataProviders::cross(
        [[1], [2], [3]],
        [['Rob'], ['John'], ['Dennis']]
    );
}
Danon
  • 2,771
  • 27
  • 37
3

You can add a comment to your dataProvider array, to provide the same functionality, while not requiring multiple dataProviders.

public static function DataProvider()
{
    return array(
        'Invalid Id'      => array(123),
        'Failed Id'       => array(321),
        'Id Not Provided' => array(NULL),
);
}
Steven Scott
  • 10,234
  • 9
  • 69
  • 117
0

well,you could consider it from another side ;) you know exactly what is your expected,for example getById(1) expected result is $result_expected,not $result_null so,you could make a dataprovider like this

$dataProvider = array(1, 'unexpected');

then,your test method like this:

public function testGetById($id) {
    $this->assertEquals($result_expected, $obj::getById($id));
}

so,test result is:

.F
mly
  • 21
  • 4