spatie/phpunit-snapshot-assertions
Add snapshot testing to PHPUnit. Assert strings, JSON, arrays, and objects against saved snapshots; first run writes snapshots, later runs compare and fail on regressions. Simple trait-based API (e.g., assertMatchesJsonSnapshot) for PHP and Laravel projects.
composer require --dev spatie/phpunit-snapshot-assertions
use Spatie\Snapshots\MatchesSnapshots;
class ExampleTest
{
use MatchesSnapshots;
public function test_basic_snapshot()
{
$this->assertMatchesSnapshot('expected output');
}
}
./vendor/bin/phpunit to generate the initial snapshot (test will be marked incomplete).
./vendor/bin/phpunit
public function test_api_response()
{
$response = $this->get('/api/users/1');
$this->assertMatchesJsonSnapshot($response->getContent());
}
JSON API Responses:
public function test_user_profile()
{
$response = $this->actingAs(User::first())
->getJson('/api/profile');
$this->assertMatchesJsonSnapshot($response->getContent());
}
HTML Email Templates:
public function test_welcome_email()
{
$email = $this->mail->assertSent(WelcomeEmail::class)
->first()
->rendered();
$this->assertMatchesHtmlSnapshot($email);
}
CLI Output:
public function test_artisan_command_output()
{
$output = $this->artisan('command:name', ['--option' => 'value'])
->output();
$this->assertMatchesTextSnapshot($output);
}
Named Snapshots for Organization:
public function test_user_serialization()
{
$user = User::factory()->create();
$this->assertMatchesSnapshot($user->toArray(), 'user-serialization');
}
Custom Snapshot Directory:
protected function getSnapshotDirectory(): string
{
return __DIR__ . '/../snapshots';
}
Parallel Testing:
CREATE_SNAPSHOTS=false php artisan test --parallel
CI Integration:
// composer.json
{
"scripts": {
"test": "php artisan test",
"test:ci": "CREATE_SNAPSHOTS=false php artisan test"
}
}
Windows Line Endings:
git config --global core.autocrlf input
.gitattributes:
__snapshots__/*.txt diff=crlf
Binary File Snapshots:
assertMatchesFileHashSnapshot() for large files (faster, less disk space)assertMatchesFileSnapshot() for visual comparison (slower, stores full file)Image Testing:
spatie/pixelmatch-php:
composer require --dev spatie/pixelmatch-php
CI False Positives:
CREATE_SNAPSHOTS=false in CI to fail on missing snapshotsUPDATE_SNAPSHOTS=true locally when intentional changes occurSnapshot Diffs:
diff __snapshots__/Test__test_method__1.txt actual_file.txt
Custom Drivers:
Driver interface for complex data typesclass CollectionDriver implements Driver
{
public function serialize($data): string
{
return json_encode($data->toArray(), JSON_PRETTY_PRINT);
}
public function extension(): string
{
return 'json';
}
public function match($expected, $actual)
{
Assert::assertEquals(json_decode($expected, true), json_decode($actual, true));
}
}
Snapshot ID Conflicts:
getSnapshotId() for consistent IDs across test runsprotected function getSnapshotId(): string
{
return (new ReflectionClass($this))->getShortName() .
'__' . date('YmdHis') .
'__' . $this->snapshotIncrementor;
}
Custom Matchers:
$this->assertMatchesSnapshot($data, null, 'custom-matcher', function($expected, $actual) {
return hash_equals($expected, $actual);
});
Pre/Post Processing:
protected function getSnapshotId(): string
{
$id = parent::getSnapshotId();
return str_replace('Test', 'Snapshot', $id);
}
Environment-Specific Snapshots:
protected function getSnapshotDirectory(): string
{
return __DIR__ . '/../../storage/snapshots/' .
config('app.env') . '/';
}
Laravel-Specific:
use Spatie\Snapshots\MatchesSnapshots;
it('matches snapshot', function () {
$this->assertMatchesSnapshot('data');
})->uses(MatchesSnapshots::class);
How can I help you explore Laravel packages today?