Mockups en MVC
En C#, el uso de mocks es común en pruebas unitarias, especialmente al probar métodos que dependen de servicios externos, bases de datos o cualquier otro componente que no se desee invocar directamente durante la prueba. Una de las bibliotecas más populares para mocking en .NET es Moq.
¿Qué es Mocking y por qué se usa?
Mocking es la práctica de crear objetos simulados que imitan el comportamiento de los objetos reales. Se usa en pruebas unitarias para:
- Aislar la unidad de código que se prueba.
- Evitar la ejecución de dependencias externas o lentas, como llamadas a la base de datos.
- Verificar interacciones, como si se llama a un método específico o si se pasan ciertos argumentos.
Instalación de Moq
Para usar Moq, primero debes instalar el paquete Moq. Si usas .NET CLI:
dotnet add package Moq
Ejemplos de uso de Mock en pruebas unitarias con Moq
Supongamos que tenemos una clase ServicioCliente que depende de una interfaz IRepositorioCliente para acceder a datos.
Código base
public interface IRepositorioCliente
{
Cliente ObtenerClientePorId(int id);
}
public class ServicioCliente
{
private readonly IRepositorioCliente _repositorioCliente;
public ServicioCliente(IRepositorioCliente repositorioCliente)
{
_repositorioCliente = repositorioCliente;
}
public string ObtenerNombreCliente(int id)
{
var cliente = _repositorioCliente.ObtenerClientePorId(id);
return cliente != null ? cliente.Nombre : "Cliente no encontrado";
}
}
public class Cliente
{
public int Id { get; set; }
public string Nombre { get; set; }
}
Prueba unitaria usando Mock con Moq
En esta prueba, queremos verificar que el método ObtenerNombreCliente devuelve el nombre correcto sin llamar a una base de datos real.
using Moq;
using Xunit;
public class ServicioClienteTests
{
[Fact]
public void ObtenerNombreCliente_ClienteExiste_RetornaNombreCliente()
{
// Configurar el mock del repositorio
var mockRepositorio = new Mock<IRepositorioCliente>();
mockRepositorio.Setup(r => r.ObtenerClientePorId(1))
.Returns(new Cliente { Id = 1, Nombre = "Juan" });
// Inyectar el mock en el servicio
var servicio = new ServicioCliente(mockRepositorio.Object);
// Actuar
var nombreCliente = servicio.ObtenerNombreCliente(1);
// Afirmar
Assert.Equal("Juan", nombreCliente);
// Verificar que el método fue llamado con el parámetro específico
mockRepositorio.Verify(r => r.ObtenerClientePorId(1), Times.Once);
}
[Fact]
public void ObtenerNombreCliente_ClienteNoExiste_RetornaMensajeError()
{
var mockRepositorio = new Mock<IRepositorioCliente>();
mockRepositorio.Setup(r => r.ObtenerClientePorId(It.IsAny<int>()))
.Returns((Cliente)null);
var servicio = new ServicioCliente(mockRepositorio.Object);
var resultado = servicio.ObtenerNombreCliente(10);
Assert.Equal("Cliente no encontrado", resultado);
mockRepositorio.Verify(r => r.ObtenerClientePorId(10), Times.Once);
}
}
Explicación del ejemplo
- Crear el Mock: Se crea un mock de
IRepositorioClienteusandovar mockRepositorio = new Mock<IRepositorioCliente>();. - Configurar el Mock:
mockRepositorio.Setup(r => r.ObtenerClientePorId(1)).Returns(new Cliente { Id = 1, Nombre = "Juan" });configura el métodoObtenerClientePorIdpara que, cuando reciba el parámetro1, devuelva un objetoClienteconNombre = "Juan".- En la segunda prueba, configuramos
ObtenerClientePorIdpara que devuelvanullpara cualquierid.
- Inyectar el Mock: Pasamos
mockRepositorio.Objectal constructor deServicioCliente. Esto permite que elServicioClienteuse el mock en lugar de una implementación real. - Verificar el Resultado:
- Usamos
Assert.Equalpara comprobar queObtenerNombreClientedevuelve el nombre correcto o el mensaje de error según el caso. mockRepositorio.Verifyasegura queObtenerClientePorIdse haya llamado con el parámetro correcto y el número de veces especificado (Times.Onceen este caso).
- Usamos
Opciones Avanzadas con Moq
- Verificar llamadas específicas:
mockRepositorio.Verify(r => r.ObtenerClientePorId(It.IsAny<int>()), Times.Exactly(1)); - Excepciones simuladas:
mockRepositorio.Setup(r => r.ObtenerClientePorId(It.IsAny<int>())) .Throws(new Exception("Error en la base de datos")); - Simulación de métodos
async: Para métodos asíncronos, puedes usarReturnsAsync.mockRepositorio.Setup(r => r.ObtenerClientePorIdAsync(It.IsAny<int>())) .ReturnsAsync(new Cliente { Id = 1, Nombre = "Juan" });
Con Moq, puedes simular casi cualquier comportamiento y controlar todas las interacciones con dependencias, lo que hace que las pruebas unitarias sean más efectivas y fáciles de realizar en .NET.