Introducción: ¿Por qué escribir pruebas a nivel avanzado?
El nivel inicial de pruebas en C# generalmente se limita a simples Assert.Equal y pruebas unitarias aisladas. Sin embargo, en proyectos empresariales reales esto no es suficiente. Los errores a menudo surgen en la interacción entre componentes — al interactuar con la base de datos, APIs externas o en lógica de negocio compleja con efectos secundarios.
Las pruebas avanzadas en C# (.NET) implican saber construir una pirámide de pruebas, donde las pruebas unitarias son rápidas y aisladas, las de integración verifican interacciones reales, y las pruebas E2E simulan el comportamiento del usuario. En este artículo, exploraremos herramientas modernas: xUnit, Moq, FluentAssertions, Testcontainers y WebApplicationFactory. Aprenderá a escribir pruebas confiables que no se rompan durante la refactorización y brinden confianza en el código.
1. Pruebas Unit avanzadas: Mocks y objetos Stub
Las pruebas unitarias deben estar aisladas de las dependencias externas. A nivel avanzado, utilizamos bibliotecas para crear objetos mock, como Moq o NSubstitute.
Configuración correcta de objetos Mock
En lugar de crear stubs manualmente, use Mock.Of<T>() para una configuración rápida. Ejemplo con Moq y FluentAssertions:
public interface IEmailService{ Task SendAsync(string to, string subject);}
public class NotificationService{ private readonly IEmailService _emailService;
public NotificationService(IEmailService emailService) { _emailService = emailService; }
public async Task NotifyUserAsync(string email) { await _emailService.SendAsync(email, "Welcome!"); }}
// Test[Fact]public async Task NotifyUserAsync_ShouldSendEmail(){ // Arrange var mockEmailService = new Mock<IEmailService>(); var service = new NotificationService(mockEmailService.Object);
// Act await service.NotifyUserAsync("test@example.com");
// Assert mockEmailService.Verify( x => x.SendAsync("test@example.com", "Welcome!"), Times.Once);}Evite probar detalles de implementación
El enfoque avanzado es probar el comportamiento, no las llamadas internas. Use Verify solo para dependencias críticas. Para el resto, verifique el resultado.
2. Pruebas de integración: Base de datos y servicios externos
Las pruebas de integración verifican que varios módulos funcionen juntos. Para .NET Core/5+, la herramienta ideal es WebApplicationFactory (para ASP.NET Core) y Testcontainers (para contenerización de dependencias).
Configuración de WebApplicationFactory con BD real
En lugar de usar el proveedor InMemory (que se comporta de manera diferente a una BD real), use Testcontainers para ejecutar PostgreSQL o SQL Server en Docker:
public class IntegrationTestFixture : IAsyncLifetime{ private readonly TestcontainersContainer _dbContainer;
public string ConnectionString { get; private set; }
public IntegrationTestFixture() { _dbContainer = new TestcontainersBuilder<TestcontainersContainer>() .WithImage("postgres:15") .WithPortBinding(5432, true) .WithEnvironment("POSTGRES_USER", "test") .WithEnvironment("POSTGRES_PASSWORD", "test") .WithEnvironment("POSTGRES_DB", "testdb") .Build(); }
public async Task InitializeAsync() { await _dbContainer.StartAsync(); ConnectionString = $"Host=localhost;Port={_dbContainer.GetMappedPublicPort(5432)};" + $"Database=testdb;Username=test;Password=test"; }
public async Task DisposeAsync() { await _dbConta