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 in your Laravel/Pest project:
composer require spatie/pest-plugin-snapshots --dev
Configure snapshot storage (default: tests/__snapshots__/). No additional config is needed unless customizing paths.
First test case (string snapshot):
use function Spatie\Snapshots\assertMatchesSnapshot;
it('matches a string snapshot', function () {
$output = 'Hello, World!';
assertMatchesSnapshot($output);
});
Run the test. The package creates a snapshot file (e.g., tests/__snapshots__/TestClass/test_matches_a_string_snapshot.txt) with the output.
Update snapshots (when intentionally changing outputs):
./vendor/bin/pest --update-snapshots
it('returns a valid user response', function () {
$response = $this->getJson('/api/user');
expect($response->json())->toMatchJsonSnapshot();
});
it('renders the welcome email', function () {
$email = $this->mail->html('emails.welcome');
expect($email)->toMatchSnapshot();
});
it('renders the dashboard component', function () {
$component = new Dashboard;
expect($component->render()->toHtml())->toMatchSnapshot();
});
Assertion-Based Testing (explicit assertions):
use function Spatie\Snapshots\{assertMatchesSnapshot, assertDoesNotMatchSnapshot};
it('matches a snapshot', function () {
$data = ['name' => 'John', 'active' => true];
assertMatchesSnapshot(json_encode($data));
});
it('fails on mismatch', function () {
$data = ['name' => 'John', 'active' => false];
assertDoesNotMatchSnapshot(json_encode($data)); // Fails if snapshot exists
});
Expectation-Based Testing (Pest fluent syntax):
it('uses expectations', function () {
$output = $this->get('/report')->content();
expect($output)->toMatchSnapshot();
});
JSON-Specific Snapshots (for API responses):
it('validates JSON structure', function () {
$response = $this->getJson('/api/data');
expect($response->json())->toMatchJsonSnapshot();
});
Image Snapshots (for PDFs, canvas, or image generation):
it('matches a generated image', function () {
$image = $this->generateReportImage();
expect($image)->toMatchImageSnapshot();
});
actingAs() or withHeaders():
it('authenticated response matches snapshot', function () {
$this->actingAs($user);
expect($this->get('/dashboard')->content())->toMatchSnapshot();
});
refreshDatabase() or withDatabaseModifications() to ensure consistent snapshots:
it('snapshot with fresh database', function () {
$this->refreshDatabase();
expect($this->get('/report')->json())->toMatchJsonSnapshot();
});
with() or withTable() to generate snapshots for multiple inputs:
it('matches snapshots for different locales', function (string $locale) {
$this->withLocale($locale);
expect($this->get('/home')->content())->toMatchSnapshot();
})->with(['en', 'fr', 'es']);
pest.php:
use Spatie\Snapshots\Snapshot;
Snapshot::create()->setPath(__DIR__.'/custom_snapshots');
describe():
it('has a descriptive snapshot', function () {
$output = $this->get('/complex-report')->content();
expect($output)->toMatchSnapshot()->describe('Full report HTML with charts');
});
assertMatchesSnapshotPartial() for large outputs where only parts change:
it('matches partial snapshot', function () {
$largeOutput = $this->get('/large-data')->content();
assertMatchesSnapshotPartial($largeOutput, 'unique-section-id');
});
vscode-diff, kdiff3) in phpunit.xml:
<php>
<env name="SNAPSHOT_DIFF_TOOL" value="vscode-diff"/>
</php>
# In CI (e.g., GitHub Actions)
if [ "$UPDATE_SNAPSHOTS" = "true" ]; then
./vendor/bin/pest --update-snapshots
fi
Snapshot ID Conflicts:
v2.3.1+ which fixes ID increment regression.tests/__snapshots__/ for duplicate filenames.Non-Deterministic Data:
$json = json_encode($response->json());
$json = preg_replace('/"timestamp": "\d+"/', '"timestamp": "REDACTED"', $json);
expect($json)->toMatchJsonSnapshot();
assertMatchesSnapshotPartial() for dynamic sections.Large Files:
assertMatchesSnapshotPartial() or exclude static sections (e.g., headers/footers).Case Sensitivity:
$normalized = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_SORT_KEYS);
expect($normalized)->toMatchJsonSnapshot();
Image Snapshots:
assertMatchesImageSnapshotWithTolerance():
expect($image)->toMatchImageSnapshotWithTolerance(0.1); // 10% tolerance
tests/__snapshots__/ to inspect stored outputs../vendor/bin/pest --update-snapshots to refresh expectations.SNAPSHOT_DIFF_TOOL in .env or phpunit.xml for visual diffs:
export SNAPSHOT_DIFF_TOOL="kdiff3"
pest.php:
Snapshot::create()->setDebug(true);
.gitignore to exclude snapshot directories if needed:
tests/__snapshots__/
Snapshot::create()->setPath(__DIR__.'/custom_path');
SnapshotIdAware:
use Spatie\Snapshots\SnapshotIdAware;
class CustomTest extends TestCase implements SnapshotIdAware {
public function getSnapshotId(): string {
return 'custom_' . $this->getName();
}
}
Spatie\Snapshots\Snapshot:
use Spatie\Snapshots\Snapshot;
class CustomSnapshot extends Snapshot {
protected function getContent(): string {
$content = parent::getContent();
return str_replace('REDACTED', '[FILTERED]', $content);
}
}
it('matches factory-generated snapshot', function () {
$user = User::factory()->create();
expect($
How can I help you explore Laravel packages today?