Cómo usar Moq para facilitar las pruebas unitarias en C #

A menudo necesitamos escribir pruebas unitarias para el código que accede a un recurso externo como una base de datos o un sistema de archivos. Si dichos recursos no están disponibles, la única forma de garantizar que las pruebas se puedan ejecutar es creando objetos simulados. En esencia, al recurrir a implementaciones falsas de estas dependencias subyacentes, puede probar la interacción entre el método que se está probando y sus dependencias. Tres de los frameworks de simulación más populares para desarrolladores .Net son Rhino Mocks, Moq y NMock.

Entre estos, Moq puede ser el más flexible y fácil de usar. El marco Moq proporciona una forma elegante de configurar, probar y verificar simulacros. Este artículo presenta una discusión sobre Moq y cómo se puede usar para aislar unidades de código de sus dependencias.

Empezando con Moq

Puede usar Moq para crear objetos simulados que simulan o imitan un objeto real. Moq se puede utilizar para simular clases e interfaces. Sin embargo, existen algunas limitaciones que debe conocer. Las clases que se van a simular no pueden ser estáticas o selladas, y el método que se va a simular debe marcarse como virtual. (Tenga en cuenta que hay soluciones para estas restricciones. Puede simular un método estático aprovechando el patrón de diseño del adaptador, por ejemplo).

El primer paso para usar Moq es instalarlo para que pueda usarlo en su proyecto de prueba unitaria. Puede descargar Moq desde GitHub y agregar referencias según corresponda. Sin embargo, prefiero instalar Moq a través de NuGet porque es más fácil y es menos probable que se pierdan referencias. Puede instalar Moq mediante el siguiente comando en la línea de comandos de NuGet.

Install-Package Moq

Cómo simular interfaces usando Moq

Comencemos por burlarnos de una interfaz. La sintaxis para crear un objeto simulado utilizando la clase Mock se proporciona a continuación.

Mock mockObjectType = nuevo Mock ();

Ahora, considere la siguiente interfaz llamada IAuthor.

interfaz pública IAuthor

    {

        int Id {obtener; conjunto; }

        string FirstName {get; conjunto; }

        string LastName {get; conjunto; }

    }

Con el marco Moq, puede crear un objeto simulado, establecer valores de propiedad, especificar parámetros y devolver valores en las llamadas al método. El siguiente fragmento de código ilustra cómo puede crear una instancia desde la interfaz IAuthor usando Moq.

var mock = new Mock ();

Tenga en cuenta que la clase Mock pertenece al marco Moq y contiene un constructor genérico que acepta el tipo de interfaz que desea crear. Moq aprovecha las expresiones lambda, delegados y genéricos. Todo esto hace que el uso del marco sea muy intuitivo.

El siguiente fragmento de código muestra cómo puede simular la interfaz IAuthor y proporcionar las propiedades de la instancia simulada con los valores adecuados. Observe cómo usamos Assert para verificar los valores de las propiedades de la instancia simulada.

var autor = nuevo Mock ();

author.SetupGet (p => p.Id) .Returns (1);

author.SetupGet (p => p.FirstName) .Returns ("Joydip");

author.SetupGet (p => p.LastName) .Returns ("Kanjilal");

Assert.AreEqual ("Joydip", autor.Objeto.Primer nombre);

Assert.AreEqual ("Kanjilal", autor.Objeto.LastNombre);

Cómo simular métodos usando Moq

Consideremos ahora la siguiente clase denominada Artículo. La clase Article contiene solo un método llamado GetPublicationDate que acepta un Id. De artículo como parámetro y devuelve la fecha de publicación del artículo.

Artículo de clase pública

    {

        Public virtual DateTime GetPublicationDate (int articleId)

        {

            lanzar nueva NotImplementedException ();

        }

    }

Debido a que el método GetPublicationDate aún no está implementado en la clase Article, se ha simulado el método para devolver la fecha actual como fecha de publicación, como se muestra en el fragmento de código que se proporciona a continuación.

var mockObj = new Mock ();
mockObj.Setup (x => x.GetPublicationDate (It.IsAny ())). Returns ((int x) => DateTime.Now);

El método de configuración se utiliza para definir el comportamiento de un método que se le pasa como parámetro. En este ejemplo, se utiliza para definir el comportamiento del método GetPublicationDate. La llamada a It.IsAny()implica que el método GetPublicationDate aceptará un parámetro de tipo integer; Itse refiere a una clase estática. El método Returns se utiliza para especificar el valor de retorno del método que se especifica en la llamada al método Setup. En este ejemplo, el método de devoluciones se utiliza para especificar el valor de retorno del método como la fecha actual del sistema.

Moq le permite verificar si se llamó a un método o propiedad en particular. El siguiente fragmento de código ilustra esto.

mockObj.Verify (t => t.GetPublicationDate (It.IsAny ()));

Aquí estamos usando el método Verify para determinar si se llamó a GetPublicationDate en el objeto simulado.

Cómo simular métodos de clase base usando Moq

Considere el siguiente fragmento de código. Tenemos dos clases aquí: la clase RepositoryBase y la clase AuthorRepository que la extiende.

clase pública abstracta RepositoryBase

{

    público virtual bool IsServiceConnectionValid ()

    {

        // Algún código

    }

}

AuthorRepository de clase pública: RepositoryBase

{

    public void Save ()

    {

        si (IsServiceConnectionValid ())

        {

            // Algún código

        }

    }

}

Ahora supongamos que queremos comprobar si la conexión a la base de datos es válida. Sin embargo, es posible que no queramos probar todo el código dentro del método IsServiceConnectionValid. Por ejemplo, el método IsServiceConnectionValid puede contener código que pertenece a una biblioteca de terceros. No querríamos probar eso, ¿verdad? Aquí es donde el método CallBase en Moq viene al rescate. 

En situaciones como esta, donde tiene un método en la clase base que ha sido anulado en el tipo simulado, y necesita simular la versión base del método anulado solamente, puede dibujar en CallBase. El siguiente fragmento de código muestra cómo puede crear un objeto simulado parcial de la clase AuthorRepository estableciendo la propiedad CallBase en true.

var mockObj = new Mock () {CallBase = true};

mockObj.Setup (x => x.IsServiceConnectionValid ()). Returns (true);

El marco Moq facilita la creación de objetos simulados que imitan el comportamiento de las clases y las interfaces para las pruebas, con solo la funcionalidad que necesita. Para obtener más información sobre las pruebas con simulacros, consulte este excelente artículo de Martin Fowler.