domingo, 27 de enero de 2008

Moq en castellano, el framework de mock estilo C# 3

Si es que usaron Rhino Mocks se habrán encontrado con que tiene una "issue" medio molesta (compartida con TypeMock.net") el patrón Record/Reply/Verify, donde hay que hacer "Record" al comenzar los expectation (o sea todo lo que deseamos que nuestros mock objects hagan al ser invocados) antes de los assert hacer "Reply" y al final "Verify", si bien no es una tortura china, es un poco antinatural, bien, ya no;

Mocks + Linq = Moq

de la mano del gran Daniel Cazzulino (alias "Kzu") llega la solución y con un agregado interesantísimo: Linq, el proyecto es llamado Moq y es un framework para "moquear" basado en Rhino Mock (incluso utiliza Castle internamente como éste) pero sin el modelo RRV y aprovechando las bondades de los tipos anónimos y las expresiones lambda, pero basta de chachara, veamos un ejemplo:

El típico Usuario /UsuarioManager /DataAccess, en donde:

- Usuario es una entidad
- UsuarioManager nos permite recuperar, guardar, manipular, etc. objetos Usuario
- DataAccess es un gestor para acceso a datos

Bien, para realizar su tarea UsuarioManager necesita echar mano de DataAccess por lo tanto en su constructor acepta un objeto que implemente la interfaz IDataAccess del siguiente modo:

public class UsuarioManager
{
    private IDataAccess _dataAccess;

    public Usuario Get(int nroDni)
    {
        return _dataAccess.GetUsuario(nroDni);
    }

    public UsuarioManager(IDataAccess dataAccess)
    {
        _dataAccess = dataAccess;
    }
}

Una de las clases mejor diseñadas que se han visto ( :-P ), bueno ahora la interfaz IDataAccess
public interface IDataAccess
{
    Usuario GetUsuario(int nroDni);
    int CantidadRecuperada { get; set; }
}
Y la clase Usuario

public class Usuario
{
    public int Edad { get; set; }
    public string Nombre { get; set; }
    public string Apellido { get; set; }

    public override bool Equals(object obj)
    {
        Usuario source = (Usuario) obj;
        
        return (
            source.Edad.Equals(this.Edad)
            && source.Apellido.Equals(this.Apellido)
            && source.Nombre.Equals(this.Nombre));

    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}
Maravilloso, vamos  a ver cómo serían las pruebas para Rhino (utilizando la sintáxis basada en using) y con Moq (voy a usar C# 2008 Express y NUnit)

[Test]
public void TestRhino()
{
    Rhino.Mocks.MockRepository mock = new Rhino.Mocks.MockRepository();
    IDataAccess dataAccessMock = mock.CreateMock<IDataAccess>();

    Usuario usuarioRetorno = new Usuario();
    usuarioRetorno.Nombre = "Leonardo";
    usuarioRetorno.Apellido = "Micheloni";
    usuarioRetorno.Edad = 32;

    using (mock.Record())
    {
        Rhino.Mocks.Expect.Call(dataAccessMock.GetUsuario(1)).Return(usuarioRetorno);
        Rhino.Mocks.Expect.Call(dataAccessMock.CantidadRecuperada).Return(10);
    }

    Usuario leonardo = new Usuario();
    leonardo.Nombre = "Leonardo";
    leonardo.Apellido = "Micheloni";
    leonardo.Edad = 32;

    using (mock.Playback())
    {
        Assert.AreEqual(leonardo, dataAccessMock.GetUsuario(1), "No se recuperan los usuarios correctamente");
        Assert.AreEqual(10, dataAccessMock.CantidadRecuperada, "No se recupera la cantidad correctamente");
    }
}

[Test]
public void TestMoq()
{
    var mock = new Mock<IDataAccess>();

    mock.Expect(x => x.GetUsuario(1)).Returns(new Usuario() { Nombre = "Leonardo", Apellido = "Micheloni", Edad = 32 });
    mock.Expect(x => x.CantidadRecuperada).Returns(10);

    Usuario leonardo = new Usuario()
    {
        Nombre = "Leonardo",
        Apellido = "Micheloni",
        Edad = 32
    };

    Assert.AreEqual(leonardo, mock.Object.GetUsuario(1), "No se recuperan los usuarios correctamente");
    Assert.AreEqual(10, mock.Object.CantidadRecuperada, "No se recupera la cantidad correctamente");
}
No es necesario que digamos cuál de los dos es más natural, compacto y elegante además. Como recomendación para quienes se encuentren en Buenos Aíres la primer semana de Febrero no dejen de ir a ver la charla que va a dar Ku acerca de Moq.

Conclusión

Sin dudas Moq es un proyecto para tener en cuenta, algo interesante es que fue escrito utilizando prácticas de eXteme Programming en un par de horas!!, el código compacto, las expresiones que se pueden utilizar con lambda y muchas features que tiene y va a seguir teniendo (no se olviden que es un proyecto que tiene poco más de un mes de vida) sin duda lo hacen algo serio. Es un lástima que en trabajo no usemos C# 3.0 todabía, por suerte en mis proyectos personales pienso usarlo todo lo que pueda :-) , saludos.

Referencias

Blog de Daniel Cazzulino
Moq is born
Moq en Google Code
Moq Quick Start