The runner, the assertions and the extensions are PHPUnit’s, which means adopting it is a syntax decision rather than a testing-framework migration.
it('rejects a negative quantity', function () {
expect(fn () => new OrderLine(sku: 'A', quantity: -1))
->toThrow(InvalidArgumentException::class);
});
dataset('invalid quantities', [-1, 0, PHP_INT_MIN]);
// and the mixed codebase, which is supported:
// tests/Unit/OrderTest.php — a PHPUnit class
// tests/Unit/LineTest.php — Pest, in the same suite
Both styles running in one suite is what makes a trial cheap and what makes a half-finished migration comfortable enough to leave — which is the actual risk. The tooling gap is the other cost: static analysis, coverage annotations and IDE navigation all understand the class form better, and the difference narrows every year.