DEV Community

Cover image for Ingeniería de Software - Test Unitarios. Cómo tratar dependencias de BD con Base de Datos en memoria y Entity Framework Core
Eduardo Barrios
Eduardo Barrios

Posted on

Ingeniería de Software - Test Unitarios. Cómo tratar dependencias de BD con Base de Datos en memoria y Entity Framework Core

Comúnmente en ingeniería de software es necesario escribir código que comprueba la correcta funcionalidad de ciertos componentes de nuestros sistemas, obviamente estos deben poseer una arquitectura limpia o cualquier arquitectura de software definida bajo buenas prácticas y patrones de diseño que a su vez nos permitan probar de manera aislada cada parte de nuestro código.
Las dependencias son un tema inevitable dentro del software, estás pueden ser llamadas a una base de datos, peticiones mediante un cliente Soap o Rest, llamadas a cualquier servicio externo o recurso de Azure, etc. Pero en la etapa que concierne a las pruebas unitarias no necesitamos utilizar las dependencias reales para probar la funcionalidad y asegurar la calidad de nuestro código, es decir no necesito conectarme a la base de datos real de producción, pre-producción, qa, o desarrollo para probar que en verdad mi código inserta en la base de datos, o no necesito realmente conectarme a Azure a un servicio real como el de Azure Cosmos DB para probar las funcionalidades que existen dentro de mi código que utilizan ese servicio de Azure.
Para resolver este problema en esta fase del desarrollo de software recurrimos a utilizar Mocks, concepto que hace referencia a utilizar implementaciones fake que permiten simular cada uno de los diferentes casos o variaciones de comportamiento que podría ocurrir como resultado en nuestro código.
En este post abordaremos pruebas unitarias con el proveedor de base de datos en memoria de Entity Framework Core para tratar con una de las dependencias existentes en un proyecto que desarrolle hace algunos meses en .NET 5 (WebApi y Blazor WASM), consiste en un proyecto API con su respectiva aplicación SPA que expone información de paises y sus códigos, además está definido bajo una arquitectura limpia y buenas prácticas de código limpio y patrones de diseño.
image

Inicialmente necesitaremos tener un proyecto de Pruebas dentro de nuestra solución, personalmente me gusta utilizar un proyecto de pruebas de xUnit.
image

Después de crear nuestro proyecto de pruebas necesitaremos instalar el paquete Nuget Microsoft.EntityFrameworkCore.InMemory
utilizando la consola de administración de paquetes de Visual Studio.

Install-Package Microsoft.EntityFrameworkCore.InMemory -Version 5.0.9
Enter fullscreen mode Exit fullscreen mode

Dentro de la estructura del proyecto existe una capa llamada Infrastructure la cuál contiene el contexto de datos real de Entity Framework Core que se conecta a la base de datos, también contiene las migraciones a la base de datos, una definición de Repositorio Genérico y del patrón Unit Of Work para gestionar las operaciones de una manera centralizada hacía la base de datos. Dentro del directorio Repositories existe una implementación custom para cada objeto representado con base en una tabla de base de datos, lo que queremos en este post es poder probar la funcionalidad de los repositories de manera aislada en nuestro proyecto de pruebas, todo esto sin tener que conectarnos a la base de datos real.
image

image

OK!

Vamos al código.

  • Necesitaremos crear un contexto de Entity Framework Core el cuál utilizaremos para pasarlo como parámetro a nuestra implementación fake de UnitOfWork, para esto crearemos un directorio llamado ResourcesDatabase en la raíz del proyecto de pruebas y seguidamente crearemos una clase llamada BaseContextTest.cs. Añadiremos el siguiente código.
    image

    Nota: en la clase BaseContextTest es necesario configurar el tipo de base de datos, en este caso en memoria, para luego pasar esa configuración a una instancia de nuestro contexto real que es el que necesitamos burlar para poder probar la funcionalidad del repository.

  • También crearemos una clase para inicializar nuestra base de datos en memoria.
    image

  • Luego de preparar una implementación fake para probar la funcionalidad del repository procedemos a crear una clase llamada CountriesRepositoryTest.cs dentro de la ruta Infrastructure.Repositories.Tests/Countries y dentro de está clase podremos crear pruebas unitarias que hagan referencia a los diferentes casos de comportamiento que puedan ocurrir en el código del repository. Probaremos el caso en el que nuestro repository devuelve una lista de elementos de una tabla de la base de datos, para esto crearemos un método que haga referencia a esa prueba.
    image

    En este test cumplimos con uno de los fundamentos de los test unitarios, el patrón de las 3 A´s el cuál se organiza de la siguiente manera:

    • Arrange (Organizar/Inicializar) => Inicializa los objetos y establece los valores de los datos que vamos a utilizar en el Test que lo contiene, en nuestro test creamos e inicializamos un objeto que implemente la interface IUnitOfWork haciendo uso de la librería Moq, en la siguiente línea pasamos el contexto propio del proyecto de pruebas unitarias a nuestro objeto fake de UnitOfWork ya que este será necesario para mandárselo al método constructor de la instancia de CountriesRepository, está es la clase a la cuál queremos probar su funcionalidad.
    • Act (Actuar) => realiza la llamada al método a probar con los parámetros preparados para tal fin, en nuestro test creamos una instancia de CountriesRepository (clase que queremos probar) pasamos el objeto fake creado en el Arrange y luego invocamos al método del repository que queremos probar.
    • Assert (Confirmar/Comprobar) => comprueba que el método de pruebas ejecutado se comporta tal y como teníamos previsto que lo hiciera, en nuestro test a manera de ejemplo y aprendizaje hacemos varias aserciones aunque no es lo recomendado.

Consejo de Expertos: Al escribir test unitarios, incluye solo una aserción por prueba, ya que si agregas varias aserciones en un caso de prueba, no se garantiza que se ejecuten todas. En la mayoría de los marcos de trabajo de pruebas unitarias, cuando se produce un error en una aserción de un test unitario, las aserciones siguientes se consideran fallidas automáticamente. Esto puede ser confuso, ya que la funcionalidad en componentes que realmente son correctas, se muestran fallidas.

Procedemos a ejecutar nuestro unit test recién creado y podremos comprobar que el test pasa correctamente.
image

Conclusiones:

  • Utilizar una librería de Mocks es bastante útil en el testing de código, ya que nos evita estar creando clases para abarcar cada uno de los comportamientos o resultados que pueda tomar nuestro código y que necesitamos probar.
  • En este post vimos una de las utilidades de las bases de datos en memoria aplicada al testing de código y la facilidad con la que podemos mockear un contexto de base de datos sin necesidad de conectarnos a la base de datos real para probar la funcionalidad del acceso a datos y demás operaciones involucradas.
  • La técnica de mock de dependencias en test unitarios es una técnica profesional, avanzada y muy utilizada que existe en la ingeniería de software para escribir test unitarios cortos y precisos y poder probar componentes que están correctamente desacoplados.

Link al respositorio en GitHub
https://github.com/EbarriosCode/BackendDotnet_CleanArchitecture/tree/master

Referencias

Discussion (4)

Collapse
jpferreyra profile image
jpferreyra

Excelente artículo, Gracias!!!

Collapse
ebarrioscode profile image
Eduardo Barrios Author

Gracias, saludos

Collapse
hugoestradas profile image
Hugo Estrada S.

excelente aporte Guayo!
.
outstanding post Eduardo!

Collapse
ebarrioscode profile image
Eduardo Barrios Author

Muchas gracias Hugo, saludos.