When one rule needs many examples, write the test once and feed it rows. A data provider is a public static method returning an iterable of argument lists, connected by #[DataProvider]; string keys name each case in the report. #[TestWith([...])] puts one row inline (Subsection 4.20.13).
<?php
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Shop\Money;
final class MoneyParsingTest extends TestCase {
#[DataProvider('validPrices')]
public function testParsesADecimalString(string $input, int $cents): void {
$this->assertSame($cents, Money::fromDecimal($input)->cents);
}
public static function validPrices(): iterable {
yield 'whole dollars' => ['12', 1200];
yield 'one decimal' => ['19.9', 1990];
}
}Pest (github.com/pestphp/pest (https://github.com/pestphp/pest 11,726 )) wraps PHPUnit 79,198 in closures and expect() chains and calls providers datasets. Pest 5.2.1 (17 September 2026) installs with composer require --dev pestphp/pest and vendor/bin/pest --init (create tests/ first):
<?php
use Shop\Money;
test('adding returns a new money', function () {
expect(new Money(1999)->plus(new Money(500))->cents)->toBe(2499);
});
it('parses decimal strings', function (string $input, int $cents) {
expect(Money::fromDecimal($input)->cents)->toBe($cents);
})->with(['whole dollars' => ['12', 1200], 'one decimal' => ['19.9', 1990]]);Output
PASS Tests\Unit\MoneyTest ✓ adding returns a new money ✓ it parses decimal strings with dataset "whole dollars" ✓ it parses decimal strings with dataset "one decimal" Tests: 3 passed (3 assertions) Duration: 0.08s
Pest is Laravel 2,157 's default (Testing and Deployment). Providers run before any setUp(), so keep them free of services.