Data Providers
Sooner or later you'll want to test the same logic against several different inputs. Without data providers, that usually means copy-pasting a test method three, four, five times, changing only the input and expected value each time. A data provider lets you write the test logic once and hand it a whole table of input/expected-output pairs — PHPUnit runs the test once per row automatically.
The Problem: Repetitive Test Methods
Imagine testing an isEven() function. Without a data provider, you might end up writing
something like this — the same three lines, repeated for every case:
<?php
use PHPUnit\Framework\TestCase;
class IsEvenTest extends TestCase
{
public function testTwoIsEven()
{
$this->assertTrue(isEven(2));
}
public function testThreeIsNotEven()
{
$this->assertFalse(isEven(3));
}
// ...and so on for every case you want to cover
}
Every one of these methods is doing exactly the same thing — calling isEven() and
asserting the result. A data provider collapses all of that into one test method plus one array of
cases.
Writing a Data Provider (Modern Attribute Syntax)
On PHPUnit 10+ with PHP 8, the recommended way to attach a provider is the #[DataProvider(...)]
attribute placed directly above the test method. The provider method itself returns an array of
arrays — each inner array is one set of arguments that gets passed to the test method for one run.
<?php
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
class IsEvenTest extends TestCase
{
#[DataProvider('numberProvider')]
public function testIsEven(int $number, bool $expected)
{
$this->assertSame($expected, isEven($number));
}
public static function numberProvider(): array
{
return [
// [number, expected result]
'zero is even' => [0, true],
'two is even' => [2, true],
'three is odd' => [3, false],
'negative four is even' => [-4, true],
'negative seven is odd' => [-7, false],
];
}
}
One testIsEven() method, five data sets, five separate test runs — no copy-pasting
required. Giving each row a descriptive string key (like 'negative four is even') instead
of leaving it a plain numeric index makes failure output much easier to read.
public (or public static, which is
preferred since it doesn't depend on any test instance state), and it must return either an array of
arrays or a generator that yields arrays. Each inner array's items are passed as positional arguments
to the test method, in order.
The Older Annotation Syntax: @dataProvider
Before PHP 8 attributes existed, and still extremely common in legacy codebases and older PHPUnit
versions, the same thing is written as a @dataProvider tag inside the test method's
docblock comment. The provider method itself looks identical — only how it's attached to the test
changes.
<?php
use PHPUnit\Framework\TestCase;
class IsEvenTest extends TestCase
{
/**
* @dataProvider numberProvider
*/
public function testIsEven($number, $expected)
{
$this->assertSame($expected, isEven($number));
}
public static function numberProvider()
{
return [
[0, true],
[2, true],
[3, false],
];
}
}
You'll run into this docblock form constantly when reading older projects or tutorials, so it's worth recognizing even though the attribute form is the modern recommendation for new code on PHP 8+.
How Failures Are Reported
When one data set fails, PHPUnit doesn't just say "testIsEven failed" and leave you guessing which input caused it. It reports the specific data set — by its string key if you gave it one, otherwise by its numeric index — right alongside the test name, so you can jump straight to the failing case.
1) IsEvenTest::testIsEven with data set "three is odd" (3, false)
Failed asserting that true is identical to false.
/path/to/tests/IsEvenTest.php:10
That single line tells you exactly which row of the provider array broke, without having to hunt through a wall of duplicated test methods to find it.
| Approach | When you'll see it |
|---|---|
#[DataProvider('method')] attribute | PHPUnit 10+, PHP 8+ codebases — the modern recommendation |
@dataProvider method docblock annotation | Legacy code, older PHPUnit versions, many existing tutorials |