2009-01-06 10 views
22

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

+1

Es difícil darle una buena respuesta a su pregunta a menos que nos diga qué problema está tratando de resolver. ¿Qué estás tratando de probar exactamente? –

+1

Parece que se ha respondido aquí: http://stackoverflow.com/questions/2065796/objectcontext-never-derives-from-an-interface-how-do-you-apply-di-ioc-in-case-o –

Respuesta

20

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

Como ObjectContext no es ni abstracto ni interfaz, no es tan fácil simularlo. 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 (sé solamente TypeMock) que utilizan 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, que también tiene otros beneficios.

Personalmente me gusta Rhino.Mocks lo mejor de las herramientas freeware, pero también usamos TypeMock, que también es un gran producto (pero hay que pagar por ello).

+0

Fantástico, pasé tanto tiempo intentando simular ObjectQuery, pero nunca pensé en cambiar ObjectQuery por IQueryable en mi Repositorio, literalmente me di una palmada en la frente cuando leí esto ... – CodeKiwi

+5

Sí ... solo asegúrate de no ir bajar el nivel IEnumerable en abstracción, porque entonces LINQ comienza a comportarse de manera diferente (las consultas se ejecutarán en la memoria y no en el servidor db). –

3

¿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 el motivo por el que uso Entity Framework es exactamente no escribir interfaces para mis conexiones de bases de datos, modelos de objetos y transacciones simples de CRUD. Para escribir interfaces simuladas para mis objetos de datos y escribir objetos complejos consultables para admitir LINQ, se frustra el propósito de confiar en el Entity Framework altamente probado y confiable.

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

¿Cómo se especifica la cadena de conexión para el entorno de prueba? Dado que todas las pruebas se encuentran 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 piense en la cantidad de dolor de cabeza y dolor que esto le ahorrará.

+1

En algunos casos, estoy seguro de que el método que sugiere es totalmente apropiado. En términos de por qué es posible que desee simular: 1 - Es probable que ejecutar las pruebas en los datos en la memoria sea mucho más rápido. 2 - Conectarse a un recurso externo introduce una dependencia en la prueba que la hace menos robusta. Como digo, ninguna de estas cosas puede ser un problema para usted. –

0

Estoy de acuerdo con los demás que realmente no se puede burlar 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 use ObjectContext (por alguna razón) y desea que la unidad 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. Instalar el paquete Effort.EF6:

PM> Install-Package Effort.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); 

Allí son usted tiene su objeto en la memoria DB.

Cuestiones relacionadas