承接 goldquality/phpunit-snapshots 相关项目开发

从需求分析到上线部署,全程专人跟进,保证项目质量与交付效率

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

goldquality/phpunit-snapshots

Composer 安装命令:

composer require --dev goldquality/phpunit-snapshots

包简介

Snapshot testing with PHPUnit

README 文档

README

Snapshot testing is a testing technique that captures the output of a function/component/endpoint and saves it as a reference.

On subsequent test runs, the output is compared against the saved reference to ensure consistency and catch unexpected changes.

PAINLESS Testing

Testing the results of API responses can often be a tedious and monotonous task. It requires meticulously checking for specific fields, validating values, and ensuring the structure aligns with expectations. While powerful tools like Laravel's built-in testing utilities or Symfony's equivalent approaches provide fine-grained control, they tend to involve verbose and repetitive code.

Here's a classic example from Laravel's official documentation:

use Illuminate\Testing\Fluent\AssertableJson;

public function test_fluent_json(): void
{
    $response = $this->getJson('/users/1');
 
    $response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has('meta')
             ->has('users', 3)
             ->has('users.0', fn (AssertableJson $json) =>
                $json->where('id', 1)
                     ->where('name', 'Victoria Faith')
                     ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
                     ->missing('password')
                     ->etc()
             )
    );
}

Here's how you can simplify the same test:

namespace Tests\Feature;

use Tests\ApiTestCase;

class ExampleTest extends ApiTestCase
{
    public function test_response_ok(): void
    {
        $response = $this->getJson('/users/1');

        // Assert the response snapshot
        $this->assertSnapshot($response->collect()->toArray());   // Asserts that response content matches the test_response_ok_0.json
        // or $this->assertResponseSnapshot($response);
    }
}

Instead of manually asserting each field and its structure, this approach stores the response in a snapshot file (e.g., test_response_ok_0.json) the first time the test runs. Future test runs will compare the current response to the saved snapshot, ensuring consistency. If the response changes intentionally, updating the snapshot is a simple process, saving significant time and effort.

Benefits

  • Speed: Quickly create tests without manually managing output assertions.
  • Flexibility: Combine with another asserts, like assertDatabaseHas, assertQueue, assertCookie and others.
  • Consistency: Automatically verifies outputs against previously captured snapshots, reducing human error.
  • Focus on Differences: When a test fails, you see the exact difference between the current response and the expected snapshot, making debugging faster.
  • Framework Agnostic: Compatible with popular frameworks like Symfony, Laravel, Yii. Making it versatile for any project setup.

Comparison with competitors

phpunit-snapshots vs spatie/phpunit-snapshot-assertions:

  • phpunit-snapshots provides a robust array of masks for dynamic values, which allows for a wide set of patterns.
  • phpunit-snapshots places a protection line ("DELETE THIS ROW") in newly created snapshots to prompt user review and manual editing, which encourages careful validation of the snapshot’s correctness.

Installation

composer require --dev goldquality/phpunit-snapshots

Configuration

  1. Create or add to existing BaseTestCase e.g. ApiTestCase
<?php

namespace Tests; // laravel
//namespace App\Tests; // symfony

use GoldQuality\PHPUnitSnapshots\SnapshotAssertTrait;
use Illuminate\Testing\TestResponse;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;


abstract class ApiTestCase extends TestCase # laravel
//abstract class ApiTestCase extends KernelTestCase # symfony
{
    use SnapshotAssertTrait;

    // Additional helper method
    public function assertResponseSnapshot($response): void
    {
        $this->assertSnapshot($response->collect()->toArray()); # laravel
        //$this->assertSnapshot($response->toArray()); # symfony
    }
}

see example implementation

Usage

  1. Extend TestCase: Extend the ApiTestCase .
  2. Invoke assert method: Use the assertSnapshot() or assertResponseSnapshot() method in your test cases with the response content as an argument.
  3. Run your tests. On the initial test run, a snapshot file is created with the JSON response content.

Example of a generated snapshot file test_response_ok_0.json:

⚠️DELETE THIS ROW ⚠️
{
  "data": [
    {
      "id": 1,
      "name": "Joe Doe",
      "balance": 100.00,
      "createdAt": "1970-12-30 12:07:24"
    },
    {
      "id": "@integer@", # use of masks for dynamic values
      "name": "Martin Fowler",
      "balance": "@double@.greaterThan(10).lowerThan(50.12)", # mask conditions
      "createdAt": "@datetime@"
    }
  ]
}
  1. Edit Snapshot: Open the created file and remove the protective line "DELETE THIS ROW" at the top.
  2. Masks (Optional): Add masks to manage auto-generated values.
  3. Re-run Tests: Verify that the response contents match your established snapshots.

This method allows developers to ensure their application's output remains consistent across updates and refactoring, enhancing test coverage and reliability with minimal effort.

Examples

Symfony Example:

namespace App\Tests\Controller;

use App\Tests\ApiTestCase;

class ExampleTest extends ApiTestCase
{
    public function test_response_ok(): void
    {
        // Simulate a logged-in user
        $client = static::createClient();
        $client->loginUser($this->getTestUser()); // Replace with your method to fetch a test user
        
        // Make a JSON request
        $response = $client->request('GET', '/api/v1/users', [], [], [
            'HTTP_ACCEPT' => 'application/json',
        ]);

        // Assert the response snapshot
        $this->assertSnapshot($response->toArray()); // Asserts that response content matches the test_response_ok_0.json
        // or $this->assertResponseSnapshot($response);
        
        // Additional assertions
        $this->assertEquals(200, $response->getStatusCode());
    }
}

Laravel Example:

namespace Tests\Feature;

use Tests\ApiTestCase;

class ExampleTest extends ApiTestCase
{
    public function test_response_ok(): void
    {
        // Simulate a logged-in user
        $user = User::factory()->create();
        $this->actingAs($user);
        
        // Make a JSON request
        $response = $this->getJson('api/v1/users');

        // Assert the response snapshot
        $this->assertSnapshot($response->collect()->toArray());   // Asserts that response content matches the test_response_ok_0.json
        // or $this->assertResponseSnapshot($response);
        
        // Additional assertions
        $response->assertStatus(200);
    }
}

Available masks

  • @string@
  • @integer@
  • @number@
  • @double@
  • @boolean@
  • @time@
  • @date@
  • @datetime@
  • @timezone@ || @tz
  • @array@
  • @array_previous@ - match next array element using pattern from previous element
  • @array_previous_repeat@ - match all remaining array elements using pattern from previous element
  • @...@ - unbounded array, once used matcher will skip any further array elements
  • @null@
  • @*@ || @wildcard@
  • @uuid@
  • @ulid@
  • @json@
  • @string@||@integer@ - string OR integer
  • For more patterns, see php-matcher available patterns.

Advanced configuration

If you need more control you can extend SnapshotHandler and implement as you need.

protected function initSnapshotHandler(): SnapshotHandler 
{
   // Custom initialization logic
}

Testing

composer test

FAQ

How do snapshot files work?

The first time a test using assertSnapshot() is run, a snapshot file (in JSON format) is created, capturing the component's or function's output. In subsequent runs, the output is compared against this file. If the outputs differ, the test will fail, alerting you to unintended changes.

My test fails due to a mismatch with the snapshot; what now?

If a test fails because the output does not match the snapshot, you should:

  1. Review the differences to determine if they are expected changes.
  2. If the changes are intended, manually edit the snapshot file or delete snapshot file and re-run the test to generate a new snapshot.
  3. If the changes are unintended, investigate and fix the underlying issue in your application.

What are masks, and how do I use them?

Masks are patterns used in snapshot files to handle dynamic values (e.g., timestamps, IDs) that might change between test runs. For example, @integer@ can be used for dynamic integer values. After the first test run, you can edit the snapshot to include these masks.

Why do I need to delete the "DELETE THIS ROW" line in the snapshot?

The "DELETE THIS ROW" line serves as a protective line to remind developers to review and edit the snapshot file after its initial creation. You should remove this line to finalize the snapshot structure.

Can I customize the naming of snapshot files?

Currently, the naming of snapshot files automatically based on your test case names.

goldquality/phpunit-snapshots 适用场景与选型建议

goldquality/phpunit-snapshots 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 306 次下载、GitHub Stars 达 8, 最近一次更新时间为 2025 年 01 月 05 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「testing」 「phpunit」 「snapshot」 「assert」 「phpunit-snapshots」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 goldquality/phpunit-snapshots 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 goldquality/phpunit-snapshots 我们能提供哪些服务?
定制开发 / 二次开发

基于 goldquality/phpunit-snapshots 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 306
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 8
  • 点击次数: 18
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 8
  • Watchers: 2
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-01-05