¿Por qué mienten tus pruebas y cómo solucionarlo?
\n\nImagina: escribes código perfecto en C#, lo cubres con pruebas, la marca verde brilla, el pipeline de CI está feliz. Publicas el lanzamiento… y la producción falla. ¿Te suena? Lo más probable es que el problema no esté en el código, sino en las pruebas. No estaban probando tu código real, sino una versión de "juguete" vinculada a la base de datos, el sistema de archivos o una API externa.
\n\nBienvenido al mundo de los objetos mock y las fixtures — los dos pilares sobre los que se sostienen las pruebas unitarias de calidad en C#. Sin ellos, tus pruebas son frágiles verificaciones de integración que se rompen ante el más mínimo cambio en el entorno. Con ellos, obtienes pruebas rápidas, aisladas y confiables que realmente protegen tu código.
\n\nEn este artículo analizaremos: en qué se diferencia un mock de un stub y un fake, cómo configurar correctamente las fixtures, y mostraremos ejemplos prácticos con bibliotecas populares: Moq, NSubstitute y AutoFixture. ¡Allá vamos!
\n\nMock, Stub, Fake — ¿cuál es la diferencia? (Spoiler: es importante)
\n\nAntes de escribir código, pongámonos de acuerdo en la terminología. Muchos desarrolladores usan la palabra "mock" para todo, pero esto genera confusión. En la clasificación de Gerard Meszaros (libro "xUnit Test Patterns") hay una división clara:
\n\n- \n
- Dummy — objeto que se pasa pero nunca se usa. Solo es necesario para llenar parámetros. \n
- Stub — proporciona respuestas predefinidas a las llamadas. Verificas el estado (state verification). \n
- Fake — una implementación funcional pero simplificada (por ejemplo, InMemoryRepository en lugar de una BD real). \n
- Mock — objeto que verifica el comportamiento (behavior verification). Afirmas que un método específico fue llamado con argumentos específicos. \n
En la práctica, en C# usamos con mayor frecuencia objetos mock (a través de Moq o NSubstitute) para verificar interacciones, y fixtures (a través de AutoFixture) para generar rápidamente datos de prueba. Pero vayamos por orden.
\n\nObjetos Mock en C#: aislamos dependencias
\n\nImagina que tienes un servicio OrderService que, al realizar un pedido, envía un correo electrónico y guarda los datos en la BD. Probarlo "de verdad" significa enviar correos y escribir en la base de datos cada vez. Es lento, poco fiable y costoso.
La solución es el aislamiento de dependencias. Reemplazamos los objetos reales (EmailSender, DbContext) por objetos mock y verificamos que el servicio llame a los métodos correctos con los parámetros adecuados.
\n\nEjemplo con Moq (la biblioteca más popular)
\n\npublic interface IEmailSender\n{\n void Send(string to, string subject, string body);\n}\n\npublic interface IOrderRepository\n{\n void Save(Order order);\n}\n\npublic class OrderService\n{\n private readonly IEmailSender _emailSender;\n private readonly IOrderRepository _repository;\n\n public OrderService(IEmailSender emailSender, IOrderRepository repository)\n {\n _emailSender = emailSender;\n _repository = repository;\n }\n\n public void PlaceOrder(Order order)\n {\n // Lógica de negocio\n _repository.Save(order);\n _emailSender.Send(order.CustomerEmail, "Pedido confirmado", $"Su pedido #{order.Id} ha sido realizado.");\n }\n}\n\nAhora escribamos una prueba usando Moq:
\n\n[TestFixture]\npublic class OrderServiceTests\n{\n [Test]\n public void PlaceOrder_ShouldSendEmail_WhenOrderIsValid()\n {\n // Arrange\n var mockEmailSender = new Mock<IEmailSender>();\n var mockRepository = new Mock<IOrderRepository>();\n var service = new OrderService(mockEmailSender.Object, mockRepository.Object);\n var order = new Order { Id =