Testes em C#: Unit, Integration, E2E do A ao Z

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: Por que os testes são necessários em C#?

\n

No desenvolvimento moderno com C# (.NET), a qualidade do código não é um luxo, mas uma necessidade. Os testes não apenas ajudam a capturar bugs antes de irem para produção, mas também servem como documentação viva e aceleram a refatoração. Existem três níveis principais de testes que formam a "pirâmide de testes": Unit (unitários), Integration (integração) e E2E (ponta a ponta). Neste artigo, abordaremos cada um deles com exemplos de código real em C#.

\n\n

1. Testes Unitários: A Base da Qualidade

\n

Testes unitários verificam um fragmento isolado de código (método, classe) sem dependências externas (banco de dados, sistema de arquivos, API). No ecossistema .NET, três frameworks são populares: xUnit (recomendado para novos projetos), NUnit e MSTest.

\n\n

Princípios Fundamentais (FIRST)

\n
    \n
  • Fast — rápidos (milissegundos).
  • \n
  • Isolated — isolados (usamos mocks).
  • \n
  • Repeatable — reproduzíveis.
  • \n
  • Self-validating — com autoverificação (Assert).
  • \n
  • Timely — oportunos.
  • \n
\n\n

Exemplo de Teste Unitário com xUnit e Moq

\n

Suponha que temos um serviço para calcular desconto:

\n
public interface IDiscountService\n{\n    decimal CalculateDiscount(decimal price, bool isLoyaltyMember);\n}\n\npublic class DiscountService : IDiscountService\n{\n    public decimal CalculateDiscount(decimal price, bool isLoyaltyMember)\n    {\n        if (isLoyaltyMember) return price * 0.9m; // 10% de desconto\n        return price;\n    }\n}
\n

Teste usando Moq (para simular dependências) e xUnit:

\n
using Xunit;\nusing Moq;\n\npublic class DiscountServiceTests\n{\n    [Fact]\n    public void CalculateDiscount_WhenLoyaltyMember_ReturnsDiscountedPrice()\n    {\n        // Arrange\n        var service = new DiscountService();\n        var price = 100m;\n\n        // Act\n        var result = service.CalculateDiscount(price, true);\n\n        // Assert\n        Assert.Equal(90m, result);\n    }\n\n    [Theory]\n    [InlineData(100, false, 100)]\n    [InlineData(200, true, 180)]\n    public void CalculateDiscount_MultipleCases_ReturnsCorrectValues(decimal price, bool isLoyalty, decimal expected)\n    {\n        var service = new DiscountService();\n        var result = service.CalculateDiscount(price, isLoyalty);\n        Assert.Equal(expected, result);\n    }\n}
\n

Dica: Use o atributo [Fact] para testes únicos e [Theory] + [InlineData] para testes parametrizados.

\n\n

2. Testes de Integração: Verificando a Conexão entre Componentes

\n

Testes de integração verificam como vários módulos funcionam juntos — por exemplo, um controller, um serviço e um banco de dados real (ou seu substituto, como SQLite in-memory). No ASP.NET Core, usamos WebApplicationFactory para isso.

\n\n

Configuração do WebApplicationFactory

\n

Criaremos uma classe de teste que sobe um host de teste web:

\n
using Microsoft.AspNetCore.Mvc.Testing;\nusing Microsoft.EntityFrameworkCore;\nusing Microsoft.Extensions.DependencyInjection;\nusing MyApp.Data;\n\npublic class CustomWebApplicationFactory : WebApplicationFactory<Program>\n{\n    protected override void ConfigureWebHost(IWebHostBuilder builder)\n    {\n        builder.ConfigureServices(services =>\n        {\n            // Remove o DbContext real\n            var descriptor = services.SingleOrDefault(\n                d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));\n            if (descriptor != null) services.Remove(descriptor);\n\n            // Adiciona banco in-memory\n            services.AddDbContext<AppDbContext>(options =>\n                

Blogs

Book Recommendations