CommunityCodierung & Entwicklunggithub.com

Snapshots-Testing

Verwenden Sie Verify für Snapshot-Tests in .NET. Genehmigen Sie API-Oberflächen, HTTP-Antworten, gerenderte E-Mails und serialisierte Ausgaben. Erkennen Sie unbeabsichtigte Änderungen durch von Menschen überprüfte Basisdateien.

Was ist Snapshots-Testing?

Diese Fähigkeit ermöglicht Snapshot-Tests mit der Verify-Bibliothek in .NET-Projekten. Sie ist besonders nützlich, um Ausgaben wie gerenderte HTML-E-Mails, API-Oberflächen, HTTP-Antworten und serialisierte Objekte zu validieren. Der Workflow besteht darin, dass beim ersten Testlauf eine .received.-Datei mit der tatsächlichen Ausgabe erstellt wird. Nach einer manuellen Überprüfung erstellt der Entwickler eine .verified.-Basisdatei. Bei nachfolgenden Läufen wird die Ausgabe gegen diese Basisdatei verglichen. Die Fähigkeit unterstützt das Herausfiltern dynamischer Werte wie GUIDs und Zeitstempel, um konsistente Tests zu gewährleisten. Sie kann in CI/CD-Pipelines integriert werden, indem Prüfungen auf fehlende Basisdateien durchgeführt werden und die Ergebnisse bei Fehlschlägen als Artefakte hochgeladen werden. Die Plattformen umfassen Xunit, NUnit und MSTest, und die Fähigkeit lässt sich gut mit der MJML-E-Mail-Rendering-Pipeline kombinieren, um Regressionen in Vorlagen zu erkennen.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/snapshot-testing

Installed? Explore more Codierung & Entwicklung skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Snapshot-Testing mit Verify

Wann diese Fähigkeit einsetzen

Verwenden Sie Snapshot-Testing, wenn Sie:

  • Gerenderte Ausgaben überprüfen (HTML-E-Mails, Berichte, generierten Code)
  • Öffentliche API-Oberflächen zur Erkennung von Breaking Changes genehmigen
  • HTTP-Antwortkörper und -Header testen
  • Serialisierungsausgabe validieren
  • Unbeabsichtigte Änderungen in komplexen Objekten abfangen

Was ist Snapshot-Testing?

Snapshot-Testing erfasst die Ausgabe und vergleicht sie mit einer von Menschen genehmigten Basisdatei:

  1. Erster Lauf: Der Test generiert eine .received.-Datei mit der tatsächlichen Ausgabe
  2. Manuelle Überprüfung: Der Entwickler genehmigt sie und erstellt eine .verified.-Datei
  3. Folgende Läufe: Der Test vergleicht die Ausgabe mit der .verified.-Datei
  4. Änderungen erkannt: Der Test schlägt fehl, und ein Diff-Tool zeigt die Unterschiede zur Überprüfung an

Dies fängt unbeabsichtigte Änderungen ab, während beabsichtigte Änderungen durch explizite Genehmigung möglich sind.


Installation

Verify-Paket hinzufügen

dotnet add package Verify.Xunit
# oder für andere Testframeworks:
dotnet add package Verify.NUnit
dotnet add package Verify.MSTest

ModuleInitializer konfigurieren

Erstellen Sie eine ModuleInitializer.cs in Ihrem Testprojekt:

using System.Runtime.CompilerServices;

public static class ModuleInitializer
{
    [ModuleInitializer]
    public static void Init()
    {
        // Verwenden Sie quelltextdatei-relativen Pfad für verified-Dateien
        VerifyBase.UseProjectRelativeDirectory("Snapshots");

        // Diff-Tool konfigurieren (optional - automatisch erkannt)
        // DiffTools.UseOrder(DiffTool.Rider, DiffTool.VisualStudioCode);
    }
}

Grundlegende Verwendung

Einfache Objektüberprüfung

[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);
}

Erstellt VerifyUserDto.verified.txt:

{
  Id: user-123,
  Name: John Doe,
  Email: [email protected],
  CreatedAt: 2025-01-15T00:00:00
}

String-/HTML-Überprüfung

[Fact]
public async Task VerifyRenderedEmail()
{
    var html = await _emailRenderer.RenderAsync("Welcome", new { Name = "John" });

    // Verwenden Sie den extension-Parameter für korrekte Dateibenennung
    await Verify(html, extension: "html");
}

Erstellt VerifyRenderedEmail.verified.html – im Browser anzeigbar.


E-Mail-Vorlagentests

Verwenden Sie Verify, um unbeabsichtigte Änderungen in gerenderten E-Mail-Vorlagen abzufangen:

[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");
}

Vorteile für E-Mail-Tests:

  • Fängt CSS-/Layout-Regressionen ab
  • Erkennt defekte Vorlagenvariablen
  • Visuelle Überprüfung im Diff-Tool
  • Versionskontrolle verfolgt E-Mail-Änderungen

Genehmigung von API-Oberflächen

Verhindern Sie unbeabsichtigte Breaking Changes an öffentlichen APIs:

[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);
}

Oder verwenden Sie das dedizierte ApiApprover-Paket:

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

Erstellt .verified.txt mit der vollständigen API-Oberfläche – jede Änderung erfordert explizite Genehmigung.


HTTP-Antworttests

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

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

    // Status, Header und Body zusammen überprüfen
    await Verify(new
    {
        StatusCode = response.StatusCode,
        Headers = response.Headers
            .Where(h => h.Key.StartsWith("X-"))  // Nur benutzerdefinierte Header
            .ToDictionary(h => h.Key, h => h.Value.First()),
        Body = await response.Content.ReadAsStringAsync()
    });
}

Herausfiltern dynamischer Werte

Behandeln Sie Zeitstempel, GUIDs und andere dynamische Inhalte:

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

    return Verify(order)
        .ScrubMember("Id")  // Durch Platzhalter ersetzen
        .ScrubMember("CreatedAt");
}

Ausgabe:

{
  Id: Guid_1,
  CreatedAt: DateTime_1,
  Total: 99.99
}

Globales Herausfiltern

Konfigurieren Sie in ModuleInitializer:

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

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

Dateiorganisation

Empfohlene Struktur

tests/
  MyApp.Tests/
    Snapshots/           # Alle verified-Dateien
      EmailTests/
        WelcomeEmail.verified.html
        PasswordReset.verified.html
      ApiTests/
        GetUser.verified.txt
    EmailTests.cs
    ApiTests.cs
    ModuleInitializer.cs

.gitignore

# Verify - received-Dateien ignorieren (nur verified committen)
*.received.*

.gitattributes

# verified-Dateien als generiert behandeln (in PR-Diffs einklappen)
*.verified.txt linguist-generated=true
*.verified.html linguist-generated=true
*.verified.json linguist-generated=true

CI/CD-Integration

Fehlschlagen bei fehlenden verified-Dateien

[ModuleInitializer]
public static void Init()
{
    // In CI fehlschlagen statt Diff-Tool zu starten
    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.*

Wann Snapshot-Testing verwenden

SzenarioSnapshot-Testing verwenden?Warum
Gerendertes HTML/E-MailsJaErfasst visuelle Regressionen
API-OberflächenJaVerhindert unbeabsichtigte Änderungen
SerialisierungsausgabeJaValidiert das Übertragungsformat
Komplexe ObjektgraphenJaEinfacher als manuelle Asserts
Einfache WertprüfungenNeinNormale Asserts verwenden
GeschäftslogikNeinExplizite Asserts verwenden
LeistungstestsNeinBenchmarks verwenden

Best Practices

DO

// Aussagekräftige Testnamen verwenden - sie werden Dateinamen
[Fact]
public Task UserRegistration_WithValidData_ReturnsConfirmation()

// Dynamische Werte konsistent herausfiltern
VerifierSettings.ScrubMembersWithType<Guid>();

// extension-Parameter für Nicht-Text-Inhalte verwenden
await Verify(html, extension: "html");

// verified-Dateien in der Versionskontrolle behalten
git add *.verified.*

DON'T

// Keine zufälligen/dynamischen Daten ohne Herausfiltern überprüfen
var order = new Order { Id = Guid.NewGuid() };  // Fehlschlag bei jedem Lauf!
await Verify(order);

// Keine .received-Dateien committen
git add *.received.*  // Falsch!

// Nicht für einfache Asserts verwenden
await Verify(result.Count);  // Einfach Assert.Equal(5, result.Count) verwenden

Integration mit MJML-E-Mail-Tests

Siehe die Fähigkeit aspnetcore/transactional-emails für das vollständige Muster:

  1. MJML-Vorlagen mit {{variable}}-Platzhaltern
  2. Rendern zu HTML mit Testdaten
  3. Snapshot-Test der gerenderten Ausgabe
  4. Änderungen vor der Genehmigung im Diff-Tool überprüfen

Dies erfasst:

  • Fehlerhafte Variablensubstitution
  • CSS-/Layout-Regressionen
  • Kompatibilitätsprobleme mit E-Mail-Clients
  • Unbeabsichtigte Inhaltsänderungen

Ressourcen

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

Verwandte Skills