Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phpunit Snapshot Assertions Laravel Package

spatie/phpunit-snapshot-assertions

Add snapshot testing to PHPUnit. Save expected output (JSON, arrays, strings, etc.) on first run and automatically compare on later runs to catch regressions with minimal assertions. Includes handy traits and snapshot update workflow for tests.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Snapshot Testing Paradigm: The package aligns perfectly with modern testing practices, particularly for Laravel applications where API responses, UI components, or complex data structures (e.g., Eloquent models, JSON payloads) require regression testing. It complements unit/integration tests by automating the validation of output formats without manual assertions.
  • Laravel Compatibility: While not Laravel-specific, the package integrates seamlessly with Laravel’s testing stack (PHPUnit, Pest) and can replace verbose assertJson()/assertEquals() chains for dynamic data. It’s particularly useful for:
    • API contract testing (e.g., assertMatchesJsonSnapshot($response->json())).
    • View/email template validation (e.g., assertMatchesHtmlSnapshot($view->render())).
    • CLI command output testing (e.g., assertMatchesTextSnapshot($commandOutput)).
  • Isolation: Snapshots are scoped to test classes/files, reducing flakiness in parallel test execution (critical for Laravel’s --parallel flag).

Integration Feasibility

  • Low Friction: Installation is a single Composer command (composer require --dev spatie/phpunit-snapshot-assertions), with zero Laravel-specific configuration. The MatchesSnapshots trait requires minimal boilerplate (e.g., use Spatie\Snapshots\MatchesSnapshots).
  • Driver Flexibility: Built-in drivers (JSON, XML, YAML, HTML, etc.) cover 90% of use cases. Custom drivers can be added for niche formats (e.g., Laravel’s Collection serialization).
  • CI/CD Readiness: Environment variables (CREATE_SNAPSHOTS, UPDATE_SNAPSHOTS) enable controlled snapshot management in pipelines (e.g., fail builds on missing snapshots in CI, auto-update in dev).

Technical Risk

  • False Positives/Negatives:
    • Risk: Snapshots may mask logical errors if tests are too broad (e.g., snapshotting an entire API response instead of specific fields). Conversely, overly strict snapshots (e.g., timestamps in JSON) may cause flaky tests.
    • Mitigation: Use named snapshots for critical outputs (e.g., assertMatchesJsonSnapshot($response, 'user-profile')) and combine with partial assertions (e.g., assertJsonStructure()).
  • Storage Bloat:
    • Risk: Snapshots accumulate in __snapshots__/ over time, requiring periodic cleanup.
    • Mitigation: Leverage Git (snapshots are text files) or add a composer script to prune old snapshots (e.g., git clean -f __snapshots__).
  • Parallel Testing:
    • Risk: Race conditions if snapshots are written simultaneously in parallel test runs.
    • Mitigation: Disable snapshot creation in CI (CREATE_SNAPSHOTS=false) and use UPDATE_SNAPSHOTS in dev with sequential runs.
  • Driver Limitations:
    • Risk: Custom drivers require manual implementation (e.g., for Laravel’s HasFactory models).
    • Mitigation: Extend existing drivers (e.g., wrap json_encode() to exclude non-deterministic fields like created_at).

Key Questions

  1. Scope of Snapshots:
    • Should snapshots cover entire API responses, or focus on critical sub-sections (e.g., only data in {"data": {...}, "meta": {...}})?
    • Recommendation: Start with granular snapshots (e.g., individual endpoints) and merge as confidence grows.
  2. Snapshot Update Workflow:
    • How will the team handle snapshot updates in CI vs. local dev?
    • Recommendation: Use composer update-snapshots locally and restrict CI to CREATE_SNAPSHOTS=false.
  3. Tooling Integration:
    • Will this replace existing tools (e.g., Laravel’s assertJson()) or augment them?
    • Recommendation: Phase in snapshots for new features; retain explicit assertions for edge cases.
  4. Performance Impact:
    • Will snapshot comparisons slow down test suites?
    • Recommendation: Benchmark with phpunit --stop-on-failure; use file hash snapshots (assertMatchesFileHashSnapshot) for large binaries.
  5. Team Adoption:
    • How will developers learn to write/maintain snapshot tests?
    • Recommendation: Document snapshot conventions (e.g., naming, update policies) and pair with a workshop on snapshot testing.

Integration Approach

Stack Fit

  • PHPUnit/Pest: Native support for both test frameworks. Pest users can leverage snapshot() helper (if using pest-plugin-snapshots).
  • Laravel Ecosystem:
    • API Testing: Replace assertJson() chains with assertMatchesJsonSnapshot($response->json()).
    • View Testing: Validate Blade templates with assertMatchesHtmlSnapshot($view->render()).
    • Console/Artisan: Test command output with assertMatchesTextSnapshot($command->output()).
  • Third-Party Packages:
    • Livewire/Inertia: Snapshot rendered components (e.g., assertMatchesHtmlSnapshot($livewire->render())).
    • Mailables: Validate email templates with assertMatchesTextSnapshot($mailable->render()).

Migration Path

  1. Pilot Phase:
    • Start with 1–2 high-risk endpoints or components (e.g., user authentication flows).
    • Example:
      public function test_login_response()
      {
          $response = $this->post('/login', ['email' => 'user@example.com']);
          $this->assertMatchesJsonSnapshot($response->json(), 'login-success');
      }
      
  2. Incremental Adoption:
    • Replace repetitive assertEquals() for complex objects (e.g., Eloquent models) with assertMatchesObjectSnapshot().
    • Example:
      public function test_user_serialization()
      {
          $user = User::factory()->create();
          $this->assertMatchesObjectSnapshot($user);
      }
      
  3. Tooling Integration:
    • Add Composer scripts for snapshot management:
      {
        "scripts": {
          "test": "phpunit",
          "update-snapshots": "UPDATE_SNAPSHOTS=true phpunit",
          "clean-snapshots": "rm -rf __snapshots__"
        }
      }
      
    • Configure CI to fail on missing snapshots:
      # .github/workflows/tests.yml
      env:
        CREATE_SNAPSHOTS: false
      

Compatibility

  • Laravel Versions: Compatible with Laravel 8+ (PHPUnit 9+). For older versions, pin to spatie/phpunit-snapshot-assertions:^1.0.
  • PHPUnit Versions: Supports PHPUnit 9–10. For PHPUnit 8, use ^1.0.
  • Parallel Testing: Requires CREATE_SNAPSHOTS=false in CI (see Usage with parallel testing).
  • Windows Line Endings: Configure Git to use LF line endings (git config --global core.autocrlf input) to avoid snapshot diff issues.

Sequencing

  1. Phase 1: Local Development
    • Enable snapshot updates locally (UPDATE_SNAPSHOTS=true).
    • Document snapshot naming conventions (e.g., feature-name__output-type).
  2. Phase 2: CI Integration
    • Disable snapshot creation in CI (CREATE_SNAPSHOTS=false).
    • Add a manual approval step for snapshot updates (e.g., via GitHub PR checks).
  3. Phase 3: Full Adoption
    • Replace legacy assertions with snapshots for new features.
    • Archive old snapshots in Git (e.g., git add __snapshots__ before major releases).

Operational Impact

Maintenance

  • Snapshot Management:
    • Pros: Snapshots are self-documenting (e.g., __snapshots__/UserTest__test_profile__1.json).
    • Cons: Requires periodic cleanup (e.g., composer clean-snapshots).
    • Recommendation: Use Git to track snapshots; add a pre-release script to prune old snapshots.
  • Update Workflow:
    • Local: Run composer update-snapshots after changing test data.
    • CI: Fail builds on missing snapshots; require manual updates via PR.
  • Driver Maintenance:
    • Update drivers if Laravel changes serialization (e.g., json_encode() defaults). Example:
      // Custom driver for Laravel 10+ JSON encoding
      class LaravelJsonDriver implements Driver {
          public function serialize($data): string {
              return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
          }
          // ...
      }
      

Support

  • Debugging Failures:
    • Snapshots provide diffs for JSON/XML/HTML (e.g., --- Expected/+++ Actual).
    • For binary files (e.g., images), use assertMatchesFileSnapshot to generate side-by-side comparisons.
  • Common Issues:

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
ecotone/kafka
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata