Pest Testing 4
When to Apply
Activate this skill when:
- Creating new tests (unit, feature, or browser)
- Modifying existing tests
- Debugging test failures
- Working with browser testing or smoke testing
- Writing architecture tests or visual regression tests
Documentation
Use search-docs for detailed Pest 4 patterns and documentation.
Creating Tests
All tests must be written using Pest. Use php artisan make:test --pest {name}.
Basic Test Structure
it('is true', function () {
expect(true)
->toBeTrue();
});
The expect is on its own line with the actual expectations chained underneath.
Running Tests
Full test runs: php artisan test --parallel (distributes across processes for speed)
Quick iteration: php artisan test --compact --filter=testName
Use --parallel for final verification before committing. Don't use --parallel for single files—it adds overhead with no benefit since tests in one file run in series.
PAO output mode (Laravel 12+): By default, test output is captured and shown as a compact JSON summary. If tests crash with no output or exit code 2, use PAO_DISABLE=1 php artisan test ... to get the raw PHP/Pest output and see the actual fatal error.
Crashes without summary: If the test run scrolls through green ticks then stops abruptly with no final summary and a non-zero exit code, a PHP fatal error likely killed the process mid-suite. This typically means a class definition error (e.g. extending a final class, interface method mismatch). Run PAO_DISABLE=1 php -d display_errors=stderr vendor/bin/pest --filter=... 2>&1 on the affected test file to surface the actual fatal.
TIA (Test Impact Analysis)
TIA runs only tests affected by your changes. Configured in tests/Pest.php with pest()->tia()->directory('tests/.pest-tia-cache')->defaultBranch('master')->locally(). The graph is committed to Git on master so worktrees and fresh clones get fast first runs.
- Master worktree: Changes visible in
git status. To update the baseline: runupdate-tia-baseline(orupdate-tia-baseline --pushto push immediately). - Non-master worktrees:
--skip-worktreeis applied automatically bysetup-worktree, hiding TIA changes fromgit status.
Script: update-tia-baseline (in tool-scripts/worktree-setup/).
Assertions
See references/assertions.md for essential guidance on assertion chaining patterns.
Use specific assertions (assertSuccessful(), assertNotFound()) instead of assertStatus():
it('returns all', function () {
$this->postJson('/api/docs', [])->assertSuccessful();
});
| Use | Instead of |
|---|---|
assertSuccessful() |
assertStatus(200) |
assertNotFound() |
assertStatus(404) |
assertForbidden() |
assertStatus(403) |
Higher-Order Expectations
When asserting multiple properties on an object, set the subject to the root object and use higher-order expectations:
it('user has correct data', function () {
$user = User::factory()->create(['name' => 'John', 'email' => 'john@example.com']);
expect($user)
->name->toBe('John')
->email->toBe('john@example.com');
});
Datasets
Use datasets for repetitive tests (validation rules, etc.):
it('has emails', function (string $email) {
expect($email)->not->toBeEmpty();
})->with([
'james' => 'james@laravel.com',
'taylor' => 'taylor@laravel.com',
]);
Reference Files
- Test Organization - Directory structure, feature vs unit tests
- Assertions - Chaining patterns and best practices
- Deep Testing - Testing with real dependencies, when to mock
- Features - Browser testing, smoke testing, architecture testing
- Common Pitfalls - What to avoid