Testes de Snapshot com Verify

Use Verify para testes de snapshot em .NET. Aprove superfícies de API, respostas HTTP, e-mails renderizados e saídas serializadas. Detecte alterações indesejadas por meio de arquivos de baseline revisados por humanos.

O que é Testes de Snapshot com Verify?

Esta skill abrange o uso da biblioteca Verify para testes de snapshot em .NET. O teste de snapshot captura a saída de uma operação (como renderização de HTML, resposta HTTP, ou serialização de objetos complexos) e a compara com um arquivo de baseline aprovado manualmente. Na primeira execução, um arquivo `.received.` é gerado; após revisão humana, ele é promovido a `.verified.`. Execuções subsequentes comparam a nova saída com o arquivo `verified`, falhando se houver diferenças. A skill cobre instalação (Verify.Xunit, Verify.NUnit, Verify.MSTest), configuração global (ModuleInitializer), uso básico (objetos, strings/HTML), testes de template de e-mail com MJML, aprovação de superfície de API com PublicApiGenerator, testes de resposta HTTP, limpeza de valores dinâmicos (GUIDs, timestamps), organização de arquivos e integração com CI/CD (GitHub Actions). É valiosa para desenvolvedores .NET que precisam garantir que mudanças intencionais sejam aprovadas explicitamente, enquanto capturam regressões visuais, estruturais ou de conteúdo.

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

Installed? Explore more Programação e Desenvolvimento skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

O que Testes de Snapshot com Verify faz?

Quando Usar Esta Skill

Use testes de snapshot quando:

  • Verificando saída renderizada (e-mails HTML, relatórios, código gerado)
  • Aprovando superfícies de API públicas para detecção de mudanças de quebra
  • Testando corpos e cabeçalhos de respostas HTTP
  • Validando saída de serialização
  • Capturando alterações indesejadas em objetos complexos

O que é Teste de Snapshot?

Teste de snapshot captura a saída e a compara com uma linha de base aprovada por humano:

  1. Primeira execução: O teste gera um arquivo .received. com a saída real
  2. Revisão humana: O desenvolvedor aprova, criando um arquivo .verified.
  3. Execuções subsequentes: O teste compara a saída com o arquivo .verified.
  4. Mudanças detectadas: O teste falha, e a ferramenta de diff mostra as diferenças para revisão

Isso captura alterações indesejadas enquanto permite alterações intencionais por meio de aprovação explícita.


Instalação

Adicionar Pacote Verify

dotnet add package Verify.Xunit
# ou para outros frameworks de teste:
dotnet add package Verify.NUnit
dotnet add package Verify.MSTest

Configurar ModuleInitializer

Crie um arquivo ModuleInitializer.cs no seu projeto de teste:

using System.Runtime.CompilerServices;

public static class ModuleInitializer
{
    [ModuleInitializer]
    public static void Init()
    {
        // Usar caminhos relativos ao arquivo fonte para arquivos verificados
        VerifyBase.UseProjectRelativeDirectory("Snapshots");

        // Configurar ferramenta de diff (opcional - detectado automaticamente)
        // DiffTools.UseOrder(DiffTool.Rider, DiffTool.VisualStudioCode);
    }
}

Uso Básico

Verificação de Objeto Simples

[Fact]
public Task VerifyUserDto()
{
    var user = new UserDto(
        Id: "user-123",
        Name: "João Silva",
        Email: "[email protected]",
        CreatedAt: new DateTime(2025, 1, 15));

    return Verify(user);
}

Cria VerifyUserDto.verified.txt:

{
  Id: user-123,
  Name: João Silva,
  Email: [email protected],
  CreatedAt: 2025-01-15T00:00:00
}

Verificação de String/HTML

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

    // Usar parâmetro de extensão para nomeação correta de arquivo
    await Verify(html, extension: "html");
}

Cria VerifyRenderedEmail.verified.html - visualizável em navegador.


Teste de Template de E-mail

Use Verify para capturar alterações indesejadas em templates de e-mail renderizados:

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

    var variables = new Dictionary<string, string>
    {
        { "OrganizationName", "Acme Corporation" },
        { "InviteeName", "João Silva" },
        { "InviterName", "Maria Admin" },
        { "InvitationLink", "https://exemplo.com/invite/abc123" },
        { "ExpirationDate", "31 de dezembro de 2025" }
    };

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

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

Benefícios para teste de e-mail:

  • Captura regressões de CSS/layout
  • Detecta quebras em variáveis de template
  • Revisão visual na ferramenta de diff
  • Controle de versão rastreia alterações de e-mail

Aprovação de Superfície de API

Evite mudanças de quebra acidentais em APIs públicas:

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

Ou use o pacote dedicado ApiApprover:

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

Cria .verified.txt com toda a superfície da API - qualquer alteração requer aprovação explícita.


Teste de Resposta HTTP

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

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

    // Verificar status, cabeçalhos e corpo juntos
    await Verify(new
    {
        StatusCode = response.StatusCode,
        Headers = response.Headers
            .Where(h => h.Key.StartsWith("X-"))  // Apenas cabeçalhos personalizados
            .ToDictionary(h => h.Key, h => h.Value.First()),
        Body = await response.Content.ReadAsStringAsync()
    });
}

Limpeza de Valores Dinâmicos

Lide com timestamps, GUIDs e outro conteúdo dinâmico:

[Fact]
public Task VerifyOrder()
{
    var order = new Order
    {
        Id = Guid.NewGuid(),  // Diferente a cada execução
        CreatedAt = DateTime.UtcNow,  // Diferente a cada execução
        Total = 99.99m
    };

    return Verify(order)
        .ScrubMember("Id")  // Substituir por placeholder
        .ScrubMember("CreatedAt");
}

Saída:

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

Limpeza Global

Configure em ModuleInitializer:

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

    // Limpar padrões específicos
    VerifierSettings.AddScrubber(s =>
        Regex.Replace(s, @"token=[a-zA-Z0-9]+", "token=SCRUBBED"));
}

Organização de Arquivos

Estrutura Recomendada

tests/
  MyApp.Tests/
    Snapshots/           # Todos os arquivos verificados
      EmailTests/
        WelcomeEmail.verified.html
        PasswordReset.verified.html
      ApiTests/
        GetUser.verified.txt
    EmailTests.cs
    ApiTests.cs
    ModuleInitializer.cs

.gitignore

# Verify - ignorar arquivos received (commitar apenas verified)
*.received.*

.gitattributes

# Tratar arquivos verified como gerados (colapsar em diffs de PR)
*.verified.txt linguist-generated=true
*.verified.html linguist-generated=true
*.verified.json linguist-generated=true

Integração com CI/CD

Falhar na Ausência de Arquivos Verified

[ModuleInitializer]
public static void Init()
{
    // Em CI, falhar em vez de abrir ferramenta de diff
    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.*

Quando Usar Testes de Snapshot

CenárioUsar Teste de Snapshot?Por que?
HTML/e-mails renderizadosSimCaptura regressões visuais
Superfícies de APISimPrevine quebras acidentais
Saída de serializaçãoSimValida formato de transmissão
Grafos de objetos complexosSimMais fácil que asserções manuais
Verificações de valor simplesNãoUse asserções regulares
Lógica de negócioNãoUse asserções explícitas
Testes de performanceNãoUse benchmarks

Melhores Práticas

FAÇA

// Use nomes de teste descritivos - eles se tornam nomes de arquivo
[Fact]
public Task UserRegistration_WithValidData_ReturnsConfirmation()

// Limpe valores dinâmicos consistentemente
VerifierSettings.ScrubMembersWithType<Guid>();

// Use parâmetro de extensão para conteúdo não textual
await Verify(html, extension: "html");

// Mantenha arquivos verified no controle de versão
git add *.verified.*

NÃO FAÇA

// Não verifique dados aleatórios/dinâmicos sem limpeza
var order = new Order { Id = Guid.NewGuid() };  // Falha toda execução!
await Verify(order);

// Não commitar arquivos .received
git add *.received.*  // Errado!

// Não use para asserções simples
await Verify(result.Count);  // Apenas use Assert.Equal(5, result.Count)

Integração com Teste de E-mail MJML

Consulte a skill aspnetcore/transactional-emails para o padrão completo:

  1. Templates MJML com placeholders {{variable}}
  2. Renderizar para HTML com dados de teste
  3. Testar snapshot da saída renderizada
  4. Revisar alterações na ferramenta de diff antes de aprovar

Isso captura:

  • Quebra de substituição de variáveis
  • Regressões de CSS/layout
  • Problemas de compatibilidade com clientes de e-mail
  • Alterações indesejadas de conteúdo

Recursos

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.

Habilidades Relacionadas