Pruebas unitarias DbContext

Investigué información sobre técnicas que podría usar para probar un DbContext . Me gustaría agregar algunos datos en memoria al contexto para que mis pruebas puedan ejecutarse en su contra. Estoy usando el enfoque de Base de datos.

Los dos artículos que he encontrado más útiles fueron esto y esto . Ese enfoque se basa en la creación de una interfaz IContext que implementarán tanto MyContext como FakeContext, lo que permite simular el contexto.

Sin embargo, estoy tratando de evitar el uso de repositorys para abstraer EF, como apuntan algunas personas, ya que EF 4.1 ya implementa repository y patrones de unidad de trabajo a través de DbSet y DbContext, y realmente me gustaría preservar todas las características implementadas por EF Equipo sin tener que mantenerlos yo mismo con un repository genérico, como ya hice en otro proyecto (y fue un poco doloroso).

Trabajar con un IContext me llevará al mismo camino (¿o no?).

Pensé en crear un FakeContext que herede de MyContext principal y así aprovechar el DbContext debajo de él para ejecutar mis pruebas sin golpear la base de datos. No pude encontrar implementaciones similares, así que espero que alguien me pueda ayudar en esto.

¿Estoy haciendo algo mal, o podría llevarme a algunos problemas que no estoy anticipando?

Hágase una sola pregunta: ¿qué vas a probar?

Mencionaste FakeContext y Mocking el contexto: ¿por qué usar ambos? Esas son simplemente formas diferentes de hacer lo mismo: proporcionar una implementación de solo prueba del contexto.

Hay un problema más grande: simular o burlarse del contexto o el conjunto tiene un solo resultado: ya no está probando su código real.

Ejemplo simple:

 public interface IContext : IDisposable { IDbSet MyEntities { get; } } public class MyEntity { public int Id { get; set; } public string Path { get; set; } } public class MyService { private bool MyVerySpecialNetMethod(e) { return File.Exists(e.Path); } public IEnumerable GetMyEntities() { using (IContext context = CreateContext()) { return context.MyEntities .Where(e => MyVerySpecialNetMethod(e)) .Select(e) .ToList(); } } } 

Ahora imagine que tiene esto en su SUT (sistema bajo prueba; en el caso de la prueba unitaria, es una unidad = generalmente un método). En el código de prueba, usted proporciona FakeContext y FakeSet y funcionará; tendrá una prueba verde. Ahora, en el código de producción, proporcionará otro DbContext y DbSet derivados y obtendrá una excepción en el tiempo de ejecución.

¿Por qué? Porque al usar FakeContext también ha cambiado el proveedor de LINQ y en lugar de LINQ a Entidades está ejecutando LINQ en Objetos, por lo que llamar a los métodos .NET locales que no se pueden convertir a SQL funciona tan bien como muchas otras características de LINQ que no están disponibles en LINQ para Entidades ! También hay otros problemas que puede encontrar con la modificación de datos: integridad referencial, eliminaciones en cascada, etc. Esa es la razón por la que creo que el código que trata con contexto / LINQ a entidades debe cubrirse con pruebas de integración y ejecutarse contra la base de datos real.

A partir de EF 4.3, puede probar el código de su unidad inyectando una DefaultConnectionFactory falsa antes de crear el contexto.

Estoy desarrollando una biblioteca de código abierto para resolver este problema.

http://effort.codeplex.com

Un pequeño adelanto

No tiene que agregar ningún código repetitivo, simplemente llame al API apropiado de la biblioteca, por ejemplo:

 var context = Effort.ObjectContextFactory.CreateTransient(); 

Al principio, esto podría parecer mágico, pero el objeto ObjectContext creado se comunicará con una base de datos en memoria y no hablará en absoluto con la base de datos real original. El término “transitorio” se refiere al ciclo de vida de esta base de datos, solo vive durante la presencia del objeto ObjectContext creado. Los objetos ObjectContext creados simultáneamente se comunican con instancias de bases de datos dedicadas, los datos no se comparten entre ellos. Esto permite escribir pruebas automatizadas fácilmente.

La biblioteca ofrece varias funciones para personalizar la creación: compartir datos entre instancias, establecer datos iniciales de la base de datos, crear una base de datos falsa en diferentes capas de datos … echa un vistazo al sitio del proyecto para obtener más información.

Entity Framework 4.1 está cerca de poder ser burlado en las pruebas, pero requiere un pequeño esfuerzo adicional. La plantilla T4 le proporciona una clase derivada DbContext que contiene propiedades DbSet. Las dos cosas que creo que debes simular son los objetos DbSet que devuelven estas propiedades y los usos y métodos que utilizas en la clase derivada DbContext. Ambos pueden lograrse modificando la plantilla T4.

Brent McKendrick ha mostrado los tipos de modificaciones que deben realizarse en esta publicación , pero no las modificaciones de la plantilla T4 que pueden lograrlo. Aproximadamente, estos son:

  1. Convierta las propiedades DbSet en la clase derivada DbContext en propiedades IDbSet.
  2. Agregue una sección que genere una interfaz para la clase derivada de DbContext que contenga las propiedades IDbSet y cualquier otro método (como SaveChanges) que deba simular.
  3. Implemente la nueva interfaz en la clase derivada DbContext.

Para cualquiera que todavía esté buscando respuestas, escribí una biblioteca simple para facilitar el burlarse de DbContext de una manera muy simple. Para más detalles, vea mi otra respuesta en SO a una pregunta similar: https://stackoverflow.com/a/33716219/111438 .

PD: Estoy de acuerdo con lo que dice Ladislav Mrnka, sin embargo, creo que en algunos casos es bastante inevitable que tenga que burlarse de su DbSet y ejecutar pruebas unitarias en su contra. Aunque debe tener en cuenta que no debe probar sus consultas LINQ para verificar si devuelven los datos correctos. Ahí es donde las pruebas de integración son más aplicables.