when webapi unit test net framework asp unit-testing entity-framework .net-3.5 mocking

unit-testing - webapi - unit testing controllers



¿Cómo burlarse de ObjectContext o ObjectQuery<T> en Entity Framework? (3)

Los marcos burlones básicos solo pueden crear simulaciones para interfaces y para clases abstractas (pero solo para métodos abstractos / virtuales).

Como ObjectContext no es abstracto ni interfaz, no es tan fácil burlarse de él. Sin embargo, como el contenedor de modelo concreto se genera como clase parcial (si utiliza el diseñador), puede extraer los métodos / propiedades que necesita de él a una interfaz. En su código, puede usar solo la interfaz, que luego puede simular.

Con ObjectQuery es un poco más fácil, ya que tiene una interfaz base (por ejemplo, IQueryable) que básicamente contiene todas las operaciones necesarias que generalmente necesita (y se requiere para LINQ). Por lo tanto, debe exponer IQueryable en lugar de ObjectQuery en su lógica empresarial, y puede crear simulacros para esa interfaz.

Otra alternativa es ocultar toda la lógica relacionada con el acceso a los datos en una capa separada (con una lógica mínima), probar esta capa con pruebas de integración y simularla para poder probar las otras capas.

Hay herramientas (solo conozco TypeMock ) que usan los ganchos de perfiles de .NET para generar los simulacros. Estas herramientas no están limitadas a interfaces simuladas o clases abstractas, pero con ellas se puede burlar básicamente cualquier cosa, incluidos los métodos no virtuales y estáticos. Con una herramienta de este tipo, no necesita cambiar su lógica comercial para permitir la burla.

Aunque este enfoque a veces es útil, debe tener en cuenta que extraer las dependencias de las interfaces (IoC) no solo es útil para burlarse, sino que también reduce las dependencias entre sus componentes, lo que también tiene otros beneficios.

Personalmente, me gusta Rhino.Mocks lo mejor de las herramientas de software gratuito, pero también utilizamos TypeMock , que también es un gran producto (pero hay que pagarlo).

¿Cómo burlarse de ObjectContext o ObjectQuery en Entity Framework?


¿Por qué no podemos simplemente crear el objeto de contexto real que se utilizará en nuestras pruebas? Como no queremos que nuestras pruebas afecten a la base de datos de producción, siempre podemos especificar una cadena de conexión que apunta a una base de datos de prueba. Antes de ejecutar cada prueba, construya un nuevo contexto, agregue los datos que necesitará en la prueba, proceda con la prueba de unidad, luego, en la sección de limpieza de prueba, elimine todos los registros que se crearon durante la prueba. El único efecto secundario aquí sería que los ID de incremento automático se utilizarían en la base de datos de prueba, pero dado que es una base de datos de prueba, ¿a quién le importa?

Sé que la mayoría de las respuestas sobre esta cuestión proponen usar diseños DI / IoC para crear interfaces para contextos de datos, etc. pero la razón por la que estoy usando Entity Framework es exactamente no escribir interfaces para mis conexiones de bases de datos, modelos de objetos y transacciones CRUD simples. Escribir interfaces simuladas para mis objetos de datos y escribir objetos complejos consultables para admitir LINQ, frustra el propósito de confiar en Entity Framework altamente probado y confiable.

Este patrón para pruebas unitarias no es nuevo: Ruby on Rails lo ha estado utilizando durante mucho tiempo y ha funcionado muy bien. Así como .NET proporciona EF, RoR proporciona objetos ActiveRecord y cada prueba de unidad crea los objetos que necesita, continúa con las pruebas y luego elimina todos los registros construidos.

¿Cómo especificar la cadena de conexión para el entorno de prueba? Dado que todas las pruebas están en su propio proyecto de prueba dedicado, sería suficiente agregar un nuevo archivo App.Config con una cadena de conexión para la base de datos de prueba.

Solo piensa en cuánto dolor de cabeza y dolor te salvará.


Estoy de acuerdo con los demás que realmente no puedes simular ObjectContext. Deberías usar EF DbContext porque puedes simular el DbSet subyacente. Hay bastantes publicaciones sobre cómo hacerlo. Entonces no escribiré cómo hacerlo. Sin embargo, si es absolutamente necesario utilizar ObjectContext (por alguna razón) y desea que Unit lo pruebe, puede usar la base de datos InMemory.

Primero instale este paquete Nuget: Esfuerzo (Entity Framework Fake ObjectContext Realization Tool), que utiliza NMemory como la base de datos. Instale el paquete Effort.EF6:

PM> Esfuerzo del paquete de instalación.EF6

using System; using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Core.Objects; using System.Data.Entity.Infrastructure; using Effort; public class DbContextHelper { //Fake object you can drop this if you are using your own EF context private class DbObject { public Guid Id { get; set; } public string Name { get; set; } } //Fake EF context you can switch with you own EF context private class FakeDbContext : DbContext { public FakeDbContext(DbConnection connection) : base(connection, true) { } public virtual DbSet<DbObject> DbObjects { get; set; } } private FakeDbContext _dbContext; public DbContextHelper() { //In memory DB connection DbConnection effortConnection = DbConnectionFactory.CreatePersistent("TestInstanceName"); _dbContext = new FakeDbContext(effortConnection); } //You can expose your context instead of the DbContext base type public DbContext DbContext => _dbContext; public ObjectContext ObjectContext => ((IObjectContextAdapter)_dbContext).ObjectContext; //Method to add Fake object to the fake EF context public void AddEntityWithState(string value, EntityState entityState) { DbContext.Entry(new DbObject() { Id = Guid.NewGuid(), Name = value }).State = entityState; } }

Uso:

DbContextHelper _dbContextHelper = new DbContextHelper(); _dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Added); _dbContextHelper.AddEntityWithState("added", System.Data.Entity.EntityState.Modified); var objs = _dbContextHelper.ObjectContext.GetObjectStateEntries(EntityState.Modified | EntityState.Added);

Ahí está usted tiene su objeto en la memoria DB.