Data Providers

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).

tests/Unit/MoneyParsingTest.php, one test method run once per rowPHP
<?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):

tests/Unit/MoneyTest.php in a Pest 5 projectPHP
<?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.