spatie/pest-plugin-snapshots
Adds snapshot testing to Pest via Spatie’s snapshot assertions. Compare strings or JSON against stored snapshots with helper functions or Pest expectations. Ideal for stable output/regression testing in PHP projects.
Install the package:
composer require spatie/pest-plugin-snapshots --dev
Ensure your pest.php config includes the plugin (auto-loaded by default in Pest v2+).
First test case:
use function Spatie\Snapshots\assertMatchesSnapshot;
it('matches a simple string snapshot', function () {
$output = 'Hello, World!';
assertMatchesSnapshot($output);
});
Run the test—it will generate a snapshot file in tests/Snapshots/ if missing.
Key directories:
tests/Snapshots/ (auto-created on first run).it('returns a valid user response', function () {
$response = $this->getJson('/api/users/1');
expect($response->json())->toMatchJsonSnapshot();
});
Why start here?
assertArraySubset() calls.Http\Testing\Concerns\InteractsWithHttpResponse.// String/HTML snapshots
expect($html)->toMatchSnapshot();
// JSON snapshots (preferred for APIs)
expect($response->json())->toMatchJsonSnapshot();
// Image snapshots (e.g., PDFs, canvas)
expect($image)->toMatchImageSnapshot();
Problem: Snapshots fail due to timestamps, IDs, or non-deterministic data.
Solution: Use snapshot modifiers (from phpunit-snapshot-assertions):
expect($response->json())
->toMatchJsonSnapshot()
->withModifiers(['replaceNonDeterministicData']);
Organize snapshots by test file or dataset (e.g., UserSnapshotTest.php):
// Auto-names snapshot: `tests/Snapshots/UserSnapshotTest/it_should_return_a_valid_user_response.json`
it('returns a valid user response', function () {
expect($response->json())->toMatchJsonSnapshot();
});
// Expectation (fluent, Pest-native)
expect($output)->toMatchSnapshot();
// Assertion (functional, explicit)
assertMatchesSnapshot($output);
When to use which?
it() blocks.beforeEach or setup logic where expect() isn’t idiomatic.Blade/Email Testing:
it('renders the welcome email', function () {
$mailable = new WelcomeEmail($user);
expect($mailable->render())
->toMatchSnapshot()
->withOptions(['ignoreWhitespace' => true]);
});
Livewire/Inertia Components:
it('renders the dashboard component', function () {
$component = new Dashboard;
expect($component->render())
->toMatchSnapshot();
});
API Feature Testing:
beforeEach(function () {
$this->actingAs($user);
});
it('lists paginated users', function () {
expect($this->getJson('/api/users')->json())
->toMatchJsonSnapshot();
});
Update snapshots on purpose:
./vendor/bin/pest --update-snapshots
Add to a pre-release script or manual step in your workflow.
Visual diffs in PRs: Configure GitHub/GitLab to show snapshot diffs (see phpunit-snapshot-assertions docs).
Snapshot validation in CI:
# .github/workflows/tests.yml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: ./vendor/bin/pest
Snapshot naming conventions:
Use descriptive test names (e.g., it_should_render_dashboard_with_orders).
Avoid generic names like it_should_work.
Snapshot ownership:
Assign specific files/directories to teams (e.g., tests/Snapshots/API/ for backend, tests/Snapshots/Blade/ for frontend).
Snapshot reviews: Require snapshot updates to be reviewed in PRs (treat them as test changes).
Issue: Duplicate snapshot IDs (fixed in v2.3.1) cause tests to fail with unclear errors. Fix:
composer update spatie/pest-plugin-snapshots
tests/Snapshots/ and rerun tests to regenerate IDs.Issue: Snapshots fail due to timestamps, UUIDs, or environment-specific values (e.g., URLs). Solutions:
expect($response->json())
->toMatchJsonSnapshot()
->withModifiers(['replaceNonDeterministicData']);
$json = json_decode($response->getContent(), true);
$json['created_at'] = '{{NON_DETERMINISTIC}}';
expect($json)->toMatchJsonSnapshot();
Issue: Giant JSON/XML snapshots slow down tests or bloat the repo. Fix:
data vs. meta in API responses).ignoreKeys:
expect($response->json())
->toMatchJsonSnapshot()
->withOptions(['ignoreKeys' => ['pagination', 'links']]);
Issue: Tests fail with "Permission denied" on snapshot files. Fix:
tests/Snapshots/ is writable:
mkdir -p tests/Snapshots && chmod -R 777 tests/Snapshots
tests/Snapshots to volume mounts.Issue: Conflicts with other Pest plugins (e.g., pest-plugin-laravel).
Fix:
expect() extensions).Override the default tests/Snapshots/ location:
// pest.php
use Spatie\Snapshots\SnapshotTestCase;
returnsTests()->uses(SnapshotTestCase::class)->in('tests/Snapshots/Custom');
Add context to snapshots (e.g., Laravel version, PHP unit):
it('renders with Laravel 10', function () {
expect(app()->version())->toBe('10.x');
expect($html)->toMatchSnapshot();
})->withMetadata(['laravel' => app()->version()]);
Test generated images/PDFs (requires spatie/phpunit-snapshot-assertions v5.3.1+):
it('generates a valid PDF', function () {
$pdf = PDF::loadView('invoice', ['user' => $user]);
expect($pdf->output())->toMatchImageSnapshot();
});
Tip: Use withOptions(['tolerance' => 0.1]) for minor visual differences.
Test specific keys in JSON:
expect($response->json()['data'])
->toMatchJsonSnapshot('user_data');
Note: Uses phpunit-snapshot-assertions's assertMatchesJsonSnapshotWithName().
Test factory outputs dynamically:
it('generates valid user data', function () {
expect(User::factory()->create()->toArray())
->toMatchJsonSnapshot();
});
./vendor/bin/pest --debug
./vendor/bin/pest --update-snapshots --filter="it_should_render_dashboard"
SnapshotTestCase::setCacheDirectory() to store snapshots in memory during development.How can I help you explore Laravel packages today?