Sobre Mocking, Inyección de dependencias y algo de Moq

Continuando con mi profundización en esto del TDD y de buenas practicas en el desarrollo de software, me tope con el tema del mocking, después de mucho leer y navegar en la red me encontré con este excelente aporte de la comunidad ALT.NET Hispano, donde el gran Hadi Hariri exponía los fundamentos de esto del mocking, al ver la grabación de la VAN, pues simplemente fortaleció y en parte opaco 😛 todo lo otro que ya había leído, así que pensé en hacer un resumen del video, pues ya saben, como dice mi mamá lo que se escribe no se olvida :P, les recomiendo saquen un tiempo para ver el video.

Ahora si empecemos, en cualquier proyecto con funcionalidades “fuertes”, por ejemplo, una aplicación que debe acceder a una base de datos notamos que sobre el método de una clase de Servicio necesitamos acceder a la clase que nos provee la funcionalidad para acceder a la base de datos.

Supongamos entonces un sistema que nos obtiene el valor de la suscripción a un servicio a partir de la edad del usuario, esto es  solo si la edad esta entre los 10 y los 90 años.

Una primera aproximación del código [sin pensar en TDD] luciría como la siguiente:

En la clase ClienteNegocio:

        public double ObtenerValorSuscripcion(int edadCliente)
        {
            double valorSuscripcion = 0;
            if (edadCliente >= 10 && edadCliente <= 90 )
            {
                ClienteAccesoDatos clienteAccesoDatos = new ClienteAccesoDatos();
                valorSuscripcion = clienteAccesoDatos.ObtenerValorSuscripcion(edadCliente);
            }
            return valorSuscripcion;
        }

En la clase de acceso a datos simplemente tendríamos el código necesario para conectarnos a una base de datos Sql Server por ejemplo y tomar de una de las tablas el costo de la suscripción.

Ahora bien si viéramos del lado de la prueba unitaria de la clase de acceso a datos veríamos algo como lo siguiente:

        [TestMethod]
        public void ObtenerValorSuscripcionPorEdadDelCliente()
        {
            ClienteAccesoDatos clienteAccesoDatos = new ClienteAccesoDatos();
            double valorSuscripcion = clienteAccesoDatos.ObtenerValorSuscripcion(20);
            Assert.AreEqual(200, valorSuscripcion);
        }

El código para la prueba unitaria de nuestra clase ClienteNegocio:

        [TestMethod]
        public void ObtenerValorSuscripcionConEdadClienteVeinteAnios()
        {
            ClienteNegocio clienteNegocio = new ClienteNegocio();
            double valorSuscripcion = clienteNegocio.ObtenerValorSuscripcion(20);
            Assert.AreEqual(200, valorSuscripcion);
        }

Si ejecutamos las pruebas veremos que ambas pruebas pasan… pero hay algo aquí que ya huele feo, y si, pues pasa que la prueba unitaria de nuestra clase de negocio NO es una prueba unitaria en si misma, porque sobre esta prueba estamos probando dos componentes distintos de nuestro sistema y adicionalmente accedemos a la base de datos y si llegase a fallar la prueba no sabríamos si fue por la base de datos o fue por nuestra lógica de negocio o fue por nuestra clase de acceso a datos. Este tipo de dependencias externas hacen que nuestra prueba unitaria sea ahora una prueba de integración, porque está estaría probando dos o más componentes de nuestro sistema. Pero entonces como podemos probar nuestro(s) métodos si estos dependen directamente de la clase ClienteAccesoDatos?… la respuesta esta en la inyección de dependencias.

La inyección de dependencias nos permitirá pasarle a nuestra clase de negocio el objeto u objetos necesarios para que nuestras funcionalidades trabajen de la forma que esperamos, ¿Pero como lo logramos? La inyección de dependencias la podemos trabajar de dos maneras, por propiedad y por constructor, trabajarlo como constructor nos brinda la ventaja de obligar a todas las clases que necesiten una instancia de nuestra clase de negocio a pasarnos el objeto que necesitamos para poder trabajar, y tener la forma de acceder a esta dependencia desde todos sus miembros. Siguiendo nuestro ejemplo tendríamos algo como lo siguiente:

        private readonly ClienteAccesoDatos _clienteAccesoDatos;

        public ClienteNegocio(ClienteAccesoDatos clienteAccesoDatos)
        {
            _clienteAccesoDatos = clienteAccesoDatos;
        }

        public double ObtenerValorSuscripcion(int edadCliente)
        {
            double valorSuscripcion = 0;
            if (edadCliente >= 10 && edadCliente <= 90 )
            {
                valorSuscripcion = _clienteAccesoDatos.ObtenerValorSuscripcion(edadCliente);
            }
            return valorSuscripcion;
        }

Y si miramos nuestra prueba unitaria efectivamente veríamos que en la instancia de clase negocio se pide que se pase un objeto de ClienteaccesoDatos, lo que podríamos hacer es crear uno allí mismo y pasarlo como parámetro del constructor, pero con  esto no estaríamos solucionando NADA, pues estaríamos probando nuevamente dos componentes en una prueba unitaria y como vimos, eso ya no seria una prueba unitaria.

Entonces ¿que le podemos pasar a este método?… en principio cualquier cosa que implemente de ClienteAccesoDatos seria un objeto valido para nuestro constructor, pero en mi opinión personal prefiero trabajar con abstracciones que con implementaciones, pues esto nos brinda mas flexibilidad y mayor independencia entre una clase y otra. Entonces para lograr esto nos creamos una interfaz con la misma definición de nuestra clase de acceso a datos, así:

    public interface IClienteAccesoDatos
    {
        double ObtenerValorSuscripcion(int edadCliente);
    }

Con esto entonces en el constructor ahora esperamos es un IClienteAccesoDatos, así:

        private readonly IClienteAccesoDatos _clienteAccesoDatos;

        public ClienteNegocio(IClienteAccesoDatos clienteAccesoDatos)
        {
            if (clienteAccesoDatos == null)
                throw new ArgumentNullException("clienteAccesoDatos");
            _clienteAccesoDatos = clienteAccesoDatos;
        }

Ahora a nuestra clase de negocio le podemos pasar cualquier cosa que implemente de nuestra interfaz… si, cualquier cosa, incluyendo un objeto “falso” que emule la funcionalidad de nuestra clase de acceso a datos, así:

        [TestMethod]
        public void ObtenerValorSuscripcionConEdadClienteVeinteAnios()
        {
            ClienteNegocio clienteNegocio = new ClienteNegocio(new FalsoAccesoDatos());
            double valorSuscripcion = clienteNegocio.ObtenerValorSuscripcion(20);
            Assert.AreEqual(200, valorSuscripcion);
        }

Y la definición de esta clase falsa:

    public class FalsoAccesoDatos : IClienteAccesoDatos
    {
        public double ObtenerValorSuscripcion(int edadCliente)
        {
            return 200;
        }
    }

Si ejecutamos nuestra prueba unitaria veremos que esta pasa 😀 gracias a este stub, un stub es un objeto que imita el comportamiento de otro… para facilitarnos este trabajo de estar creando objetos falsos para imitar el comportamiento y permitirnos que nuestras pruebas puedan ser llevadas a cabo de manera independiente, existen los Frameworks de Mocking, como lo es Moq.

Para empezar con Moq debemos agregar una referencia  a este, yo lo hago usando nuget:

Y con Moq podemos entonces hacer cosas como la siguiente:

        [TestMethod]
        public void ObtenerValorSuscripcionConEdadClienteVeinteAnios()
        {
            var clienteAccesoDatosMock = new Mock<IClienteAccesoDatos>();
            clienteAccesoDatosMock.Setup(x => x.ObtenerValorSuscripcion(It.IsAny<int>())).Returns(200);
            var clienteNegocio = new ClienteNegocio(clienteAccesoDatosMock.Object);
            double valorSuscripcion = clienteNegocio.ObtenerValorSuscripcion(20);
            Assert.AreEqual(200, valorSuscripcion);
        }

Como vemos creamos un stub para emular la funcionalidad de nuestra clase de acceso de a datos y poder realizar nuestra prueba unitaria como se debe.

Por hoy dejamos hasta aquí, pero espero en próximas entradas poder explicar más a fondo el tema de Moq.

Hasta el próximo post.

Anuncios
Sobre Mocking, Inyección de dependencias y algo de Moq

2 comentarios en “Sobre Mocking, Inyección de dependencias y algo de Moq

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s