mcpField
S
snapshot-testing

snapshot-testing

github.com/aarononthewebテスト・品質1.2k471 回インストール今日更新
リポジトリ

snapshot-testing について

snapshot-testing は、Verify を使う .NET 開発者向けのスキルです。人が承認した基準出力とテスト結果を比較し、描画内容、公開 API、HTTP レスポンス、シリアライズ結果を扱います。

主な機能

  1. 01承認済み出力のベースライン
  2. 02HTML とオブジェクトのスナップショット
  3. 03公開 API の変更検出
  4. 04動的な値のスクラブ
  5. 05CI でのスナップショット検査

活用シーン

  1. 01描画されたメールの変更確認
  2. 02意図しない公開 API の破壊的変更検出
  3. 03HTTP レスポンスとシリアライズ結果の検証

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/aaronontheweb/dotnet-skills --skill snapshot-testing

インストール後、Claude Code で/snapshot-testingを入力して読み込まれたか確認します。

詳細

Verify を使ったスナップショットテスト

このスキルを使う場面

次のような場合にスナップショットテストを使います。

  • レンダリングされた出力(HTML メール、レポート、生成コード)を検証する
  • 破壊的変更を検出するために、公開 API のサーフェスを承認する
  • HTTP レスポンスのボディとヘッダーをテストする
  • シリアル化された出力を検証する
  • 複雑なオブジェクトに意図しない変更がないか確認する

スナップショットテストとは?

スナップショットテストでは、出力を取得し、人が承認したベースラインと比較します。

  1. 初回実行:テストによって実際の出力を含む .received. ファイルが生成されます
  2. 人によるレビュー:開発者が内容を承認し、.verified. ファイルを作成します
  3. 以降の実行:テストで出力を .verified. ファイルと比較します
  4. 変更の検出:テストが失敗し、差分ツールにレビュー用の差分が表示されます

これにより、意図しない変更を検出しつつ、意図的な変更は明示的な承認を経て反映できます。


インストール

Verify パッケージを追加する
dotnet add package Verify.Xunit
# or for other test frameworks:
dotnet add package Verify.NUnit
dotnet add package Verify.MSTest
ModuleInitializer を設定する

テストプロジェクトに ModuleInitializer.cs を作成します。

using System.Runtime.CompilerServices;

public static class ModuleInitializer
{
    [ModuleInitializer]
    public static void Init()
    {
        // Use source-file-relative paths for verified files
        VerifyBase.UseProjectRelativeDirectory("Snapshots");

        // Configure diff tool (optional - auto-detected)
        // DiffTools.UseOrder(DiffTool.Rider, DiffTool.VisualStudioCode);
    }
}

基本的な使い方

シンプルなオブジェクトの検証
[Fact]
public Task VerifyUserDto()
{
    var user = new UserDto(
        Id: "user-123",
        Name: "John Doe",
        Email: "[email protected]",
        CreatedAt: new DateTime(2025, 1, 15));

    return Verify(user);
}

次のファイルが作成されます:VerifyUserDto.verified.txt

{
  Id: user-123,
  Name: John Doe,
  Email: [email protected],
  CreatedAt: 2025-01-15T00:00:00
}
文字列/HTML の検証
[Fact]
public async Task VerifyRenderedEmail()
{
    var html = await _emailRenderer.RenderAsync("Welcome", new { Name = "John" });

    // Use extension parameter for proper file naming
    await Verify(html, extension: "html");
}

ブラウザーで表示できる VerifyRenderedEmail.verified.html が作成されます。


メールテンプレートのテスト

Verify を使って、レンダリングされたメールテンプレートに意図しない変更がないか確認します。

[Fact]
public async Task UserSignupInvitation_RendersCorrectly()
{
    var renderer = _services.GetRequiredService<IMjmlTemplateRenderer>();

    var variables = new Dictionary<string, string>
    {
        { "OrganizationName", "Acme Corporation" },
        { "InviteeName", "John Doe" },
        { "InviterName", "Jane Admin" },
        { "InvitationLink", "https://example.com/invite/abc123" },
        { "ExpirationDate", "December 31, 2025" }
    };

    var html = await renderer.RenderTemplateAsync(
        "UserInvitations/UserSignupInvitation",
        variables);

    await Verify(html, extension: "html");
}

メールテストでのメリット:

  • CSS やレイアウトのリグレッションを検出します
  • テンプレート変数の破損を検出します
  • 差分ツールで視覚的にレビューできます
  • バージョン管理でメールの変更を追跡できます

API サーフェスの承認

公開 API に意図しない破壊的変更が生じるのを防ぎます。

[Fact]
public Task ApprovePublicApi()
{
    var assembly = typeof(MyLibrary.PublicClass).Assembly;

    var publicApi = assembly.GetExportedTypes()
        .OrderBy(t => t.FullName)
        .Select(t => new
        {
            Type = t.FullName,
            Members = t.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
                .Where(m => m.DeclaringType == t)
                .OrderBy(m => m.Name)
                .Select(m => m.ToString())
        });

    return Verify(publicApi);
}

または、専用の ApiApprover パッケージを使います。

dotnet add package PublicApiGenerator
dotnet add package Verify.Xunit
[Fact]
public Task ApproveApi()
{
    var api = typeof(MyPublicClass).Assembly.GeneratePublicApi();
    return Verify(api);
}

API サーフェス全体を含む .verified.txt が作成されます。変更にはすべて明示的な承認が必要です。


HTTP レスポンスのテスト

[Fact]
public async Task GetUser_ReturnsExpectedResponse()
{
    var client = _factory.CreateClient();

    var response = await client.GetAsync("/api/users/123");

    // Verify status, headers, and body together
    await Verify(new
    {
        StatusCode = response.StatusCode,
        Headers = response.Headers
            .Where(h => h.Key.StartsWith("X-"))  // Custom headers only
            .ToDictionary(h => h.Key, h => h.Value.First()),
        Body = await response.Content.ReadAsStringAsync()
    });
}

動的な値のスクラブ

タイムスタンプ、GUID、その他の動的なコンテンツを処理します。

[Fact]
public Task VerifyOrder()
{
    var order = new Order
    {
        Id = Guid.NewGuid(),  // Different every run
        CreatedAt = DateTime.UtcNow,  // Different every run
        Total = 99.99m
    };

    return Verify(order)
        .ScrubMember("Id")  // Replace with placeholder
        .ScrubMember("CreatedAt");
}

出力:

{
  Id: Guid_1,
  CreatedAt: DateTime_1,
  Total: 99.99
}
グローバルなスクラブ

ModuleInitializer で設定します。

[ModuleInitializer]
public static void Init()
{
    VerifierSettings.ScrubMembersWithType<DateTime>();
    VerifierSettings.ScrubMembersWithType<DateTimeOffset>();
    VerifierSettings.ScrubMembersWithType<Guid>();

    // Scrub specific patterns
    VerifierSettings.AddScrubber(s =>
        Regex.Replace(s, @"token=[a-zA-Z0-9]+", "token=SCRUBBED"));
}

ファイル構成

推奨される構成
tests/
  MyApp.Tests/
    Snapshots/           # All verified files
      EmailTests/
        WelcomeEmail.verified.html
        PasswordReset.verified.html
      ApiTests/
        GetUser.verified.txt
    EmailTests.cs
    ApiTests.cs
    ModuleInitializer.cs
.gitignore
# Verify - ignore received files (only commit verified)
*.received.*
.gitattributes
# Treat verified files as generated (collapse in PR diffs)
*.verified.txt linguist-generated=true
*.verified.html linguist-generated=true
*.verified.json linguist-generated=true

CI/CD との統合

検証済みファイルがない場合に失敗させる
[ModuleInitializer]
public static void Init()
{
    // In CI, fail instead of launching diff tool
    if (Environment.GetEnvironmentVariable("CI") == "true")
    {
        VerifyDiffPlex.UseDiffPlex(OutputType.Minimal);
        DiffRunner.Disabled = true;
    }
}
GitHub Actions
- name: Run tests
  run: dotnet test
  env:
    CI: true

- name: Upload snapshots on failure
  if: failure()
  uses: actions/upload-artifact@v4
  with:
    name: snapshots
    path: |
      **/*.received.*
      **/*.verified.*

スナップショットテストを使う場面

シナリオスナップショットテストを使うか?理由
レンダリングされた HTML/メールはい視覚的なリグレッションを検出します
API サーフェスはい意図しない破壊的変更を防ぎます
シリアル化された出力はいワイヤーフォーマットを検証します
複雑なオブジェクトグラフはい手動のアサーションより簡単です
シンプルな値のチェックいいえ通常のアサーションを使います
ビジネスロジックいいえ明示的なアサーションを使います
パフォーマンステストいいえベンチマークを使います

ベストプラクティス

すること
// Use descriptive test names - they become file names
[Fact]
public Task UserRegistration_WithValidData_ReturnsConfirmation()

// Scrub dynamic values consistently
VerifierSettings.ScrubMembersWithType<Guid>();

// Use extension parameter for non-text content
await Verify(html, extension: "html");

// Keep verified files in source control
git add *.verified.*
しないこと
// Don't verify random/dynamic data without scrubbing
var order = new Order { Id = Guid.NewGuid() };  // Fails every run!
await Verify(order);

// Don't commit .received files
git add *.received.*  // Wrong!

// Don't use for simple assertions
await Verify(result.Count);  // Just use Assert.Equal(5, result.Count)

MJML メールテストとの統合

完全なパターンについては、mjml-email-templates および verify-email-snapshots スキルを参照してください。

  1. {{variable}} プレースホルダーを使った MJML テンプレート
  2. テストデータを使って HTML にレンダリング
  3. レンダリングされた出力をスナップショットテスト
  4. 承認前に差分ツールで変更をレビュー

次の問題を検出できます。

  • 変数置換の破損
  • CSS やレイアウトのリグレッション
  • メールクライアントとの互換性の問題
  • 意図しないコンテンツの変更

リソース

コメント