Pular para conteúdo

Aula 19 - Testes Automatizados com xUnit, Moq e FluentAssertions 🧪

Objetivo Pedagógico

Objetivo: Estratégia de testes automatizados corporativos em .NET 8: Testes unitários com xUnit e Moq, asserções expressivas com FluentAssertions e testes de integração com Testcontainers.


📑 1. Fundamentos Teóricos & Análise Técnica

A confiabilidade de sistemas empresariais em .NET é garantida por uma pirâmide de testes disciplinada. A combinação da biblioteca xUnit, a ferramenta de simulação Moq e as asserções fluentes do FluentAssertions constitui o padrão de excelência para testes de software em C#.

  1. Testes Unitários com xUnit e Moq: Isolam a unidade de código sob teste (System Under Test - SUT), substituindo dependências externas (como repositórios de banco de dados ou gateways de pagamento) por objetos simulados (mocks), verificando chamadas com Verify() e configurando respostas com Setup().
  2. FluentAssertions: Substitui asserções antiquadas do tipo Assert.Equal(esperado, obtido) por uma sintaxe fluente e humanizada (resultado.Should().Be(esperado)), gerando mensagens de diagnóstico detalhadas e visualmente informativas quando um teste falha.
  3. Testes de Integração com Testcontainers: Inicializa contêineres Docker reais de banco de dados (SQL Server, PostgreSQL) durante a execução dos testes, garantindo que queries SQL complexas funcionem exatamente como esperado sem mocks artificiais.

📐 Arquitetura Conceitual & Diagrama de Fluxo

flowchart LR
    Unit["Testes Unitários (xUnit + Moq)<br>Isolamento Rápido em Memória"] --> SUT["Regra de Negócio / Service"]
    Integration["Testes de Integração (WebApplicationFactory)"] --> RealDB["Testcontainers (Docker Banco Real)"]
    SUT --> Assertions["FluentAssertions (result.Should().BeSuccess())"]
    RealDB --> Assertions
    Assertions --> Report["Relatório de Cobertura e CI Verde"]
    style Unit fill:#e1f5fe,stroke:#01579b
    style Integration fill:#fff3e0,stroke:#e65100
    style Assertions fill:#e8f5e9,stroke:#2e7d32

🔍 Pilares e Diretrizes Técnicas

Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Padrão AAA (Arrange, Act, Assert): Estruturação clara das fases de preparação, execução e validação do teste. - Simulação Estrita com Moq: Verificação determinística de que métodos críticos de auditoria ou gravação foram invocados. - Asserções Autodescritivas: O FluentAssertions explica claramente a divergência estrutural em falhas de coleções ou objetos aninhados. - Banco de Dados Real em Testes: Eliminação de divergências entre provedores in-memory e dialetos SQL de produção via Testcontainers.


🛠️ 2. Implementação Prática em C#, xUnit, Moq e Testcontainers

Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:

// OrderServiceTests.cs (Testes Unitários com xUnit, Moq e FluentAssertions)
using Xunit;
using Moq;
using FluentAssertions;

namespace Empresa.Core.Tests;

public class OrderServiceTests
{
    private final Mock<IOrderRepository> _orderRepoMock = new();
    private final Mock<IPaymentGateway> _paymentMock = new();

    [Fact]
    public async Task ProcessOrder_ValidOrder_ReturnsSuccessResult()
    {
        // 1. Arrange (Preparação)
        var orderId = Guid.NewGuid();
        _paymentMock.Setup(p => p.ChargeAsync(It.IsAny<decimal>(), It.IsAny<CancellationToken>()))
                    .ReturnsAsync(true);

        var service = new OrderProcessingService(_orderRepoMock.Object, _paymentMock.Object);

        // 2. Act (Execução)
        var result = await service.ExecuteOrderAsync(orderId, 100m, CancellationToken.None);

        // 3. Assert (Validação com FluentAssertions)
        result.IsSuccess.Should().BeTrue();
        result.Message.Should().Contain("Processado com sucesso");

        // Verifica se o pagamento foi chamado exatamente 1 vez
        _paymentMock.Verify(p => p.ChargeAsync(100m, It.IsAny<CancellationToken>()), Times.Once);
    }
}

💡 Análise Passo a Passo do Código

  1. Criação de Mocks com Moq: _paymentMock.Setup(...) programa o comportamento esperado da dependência externa de forma isolada.
  2. Sintaxe com Should(): result.IsSuccess.Should().BeTrue() expressa a intenção do teste de maneira limpa e intuitiva.
  3. Verificação de Invocação: Times.Once garante que nenhuma cobrança financeira seja efetuada em duplicidade pelo fluxo.

🎯 3. Próximos Passos & Sequência Didática