EntityFramework, DbContextScope y Effort - excepción: DbContext se ha eliminado dentro de la prueba de la unidad


Pregunta

Estoy tratando de escribir pruebas unitarias (con NUnit) para la capa de servicio que usa:

  1. Entity Framework como capa de acceso a datos
  2. DbContextScope para administrar el tiempo de vida de DbContext

También uso Effort.EF6 para burlarse de DbContext en pruebas unitarias. Desafortunadamente, no puedo encontrar una manera de hacer que DbContextScope sea compatible con Effort para poder probar correctamente todos los casos.


Resumen del código

La capa de servicio consiste en clases (servicios) que hacen algo de lógica de negocios. Cada método se trata como una transacción completa, finalizada con context.SaveChanges() . Ejemplo:

    private IDbContextScopeFactory _dbContextScopeFactory;

    public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory)
    {
        _dbContextScopeFactory = dbContextScopeFactory;
    }

    public BusinessModel.Department Insert(BusinessModel.Department department)
    {
        using (var dbContextScope = _dbContextScopeFactory.Create())
        {
            // Validation
            ValidateAndThrowOnFailure(department, new DepartmentAddValidator());

            // Operation
            DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department);

            newDepartment.InsertDateUTC = DateTime.UtcNow;

            dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment);
            dbContextScope.SaveChanges();

            return Mapper.Map<BusinessModel.Department>(newDepartment);
        }
    }

Para realizar una prueba unitaria de dicho método, hago una preparación antes de cada prueba:

    private IDbContextScopeFactory _dbContextScopeFactory;

    public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory)
    {
        _dbContextScopeFactory = dbContextScopeFactory;
    }

    public BusinessModel.Department Insert(BusinessModel.Department department)
    {
        using (var dbContextScope = _dbContextScopeFactory.Create())
        {
            // Validation
            ValidateAndThrowOnFailure(department, new DepartmentAddValidator());

            // Operation
            DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department);

            newDepartment.InsertDateUTC = DateTime.UtcNow;

            dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment);
            dbContextScope.SaveChanges();

            return Mapper.Map<BusinessModel.Department>(newDepartment);
        }
    }

La prueba y el problema.

Aquí hay una prueba unitaria simple:

    private IDbContextScopeFactory _dbContextScopeFactory;

    public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory)
    {
        _dbContextScopeFactory = dbContextScopeFactory;
    }

    public BusinessModel.Department Insert(BusinessModel.Department department)
    {
        using (var dbContextScope = _dbContextScopeFactory.Create())
        {
            // Validation
            ValidateAndThrowOnFailure(department, new DepartmentAddValidator());

            // Operation
            DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department);

            newDepartment.InsertDateUTC = DateTime.UtcNow;

            dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment);
            dbContextScope.SaveChanges();

            return Mapper.Map<BusinessModel.Department>(newDepartment);
        }
    }

El problema es que la prueba falla con excepción:

    private IDbContextScopeFactory _dbContextScopeFactory;

    public DepartmentsService(IDbContextScopeFactory dbContextScopeFactory)
    {
        _dbContextScopeFactory = dbContextScopeFactory;
    }

    public BusinessModel.Department Insert(BusinessModel.Department department)
    {
        using (var dbContextScope = _dbContextScopeFactory.Create())
        {
            // Validation
            ValidateAndThrowOnFailure(department, new DepartmentAddValidator());

            // Operation
            DBModel.Department newDepartment = Mapper.Map<DBModel.Department>(department);

            newDepartment.InsertDateUTC = DateTime.UtcNow;

            dbContextScope.DbContexts.Get<DPSContext>().Departments.Add(newDepartment);
            dbContextScope.SaveChanges();

            return Mapper.Map<BusinessModel.Department>(newDepartment);
        }
    }

Parece que DbContextScope utilizado dentro del método Insert desecha internamente los contextos asignados al final del bloque de using y, por tanto, la excepción Assert arroja cuando se invoca. ¿Alguien se ha encontrado con un problema similar o simplemente sabe qué debo hacer para probar con éxito este y otros escenarios similares?

Respuesta aceptada

Para cualquier persona que se encuentre con un problema similar, he creado una solución un poco sucia pero que funciona (al menos eso espero). Además de lo que escribí en la pregunta, creé una clase derivada del contexto real e hice que el método Dispose no hiciera ... nada. También agregué un método RealDispose que se llama al final de cada prueba.

    public class TestableDPSContext : DBModel.DPSContext
    {
        public TestableDPSContext(DbConnection connection)
            : base(connection)
        {

        }

        protected override void Dispose(bool disposing)
        {
            // Do nothing
        }

        public void RealDispose(bool disposing)
        {
            // Invoke real dispose
            base.Dispose(disposing);
        }
    }

    [TearDown]
    public void FinishTest()
    {
        _dbEntities.RealDispose(false);
    }

Tal vez haya una solución mejor, pero por ahora parece resolver el problema con la DbContext demasiado pronto en las pruebas.





Licencia bajo: CC-BY-SA
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué