EntityFramework, DbContextScope et Effort - exception: DbContext a été supprimé dans le test unitaire.

c# dbcontext effort entity-framework unit-testing

Question

J'essaie d'écrire des tests unitaires (avec NUnit) pour la couche de service qui utilise:

  1. Entity Framework en tant que couche d'accès aux données
  2. DbContextScope pour gérer la durée de vie de DbContext

J'utilise aussi Effort.EF6 pour me moquer de DbContext dans les tests unitaires. Malheureusement, je ne trouve pas le moyen de rendre DbContextScope compatible avec Effort afin de pouvoir tester correctement tous les cas.


Vue d'ensemble du code

La couche service est constituée de classes (services) qui exécutent une logique métier. Chaque méthode est traitée comme une transaction complète, terminée par context.SaveChanges() . Exemple:

    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);
        }
    }

Pour tester une telle méthode, je fais une préparation avant chaque test:

    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);
        }
    }

Le test et le problème

Voici un test unitaire 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);
        }
    }

Le problème est que le test échoue avec une exception:

    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);
        }
    }

Il semble que DbContextScope, utilisé dans la méthode Insert , dispose en interne des contextes attribués à la fin du bloc using , de sorte que l' Assert lève une exception lorsqu'il est appelé. Quelqu'un at-il rencontré un problème similaire ou sait-il simplement ce que je devrais faire pour tester avec succès ce scénario et des scénarios similaires?

Réponse acceptée

Pour ceux qui rencontrent un problème similaire, j'ai créé une solution un peu sale mais qui fonctionne (du moins je l'espère). En plus de ce que j'ai écrit dans la question, j'ai créé une classe dérivée du contexte réel et fait en sorte que la méthode Dispose ne fasse ... rien. J'ai également ajouté une méthode RealDispose appelée à la fin de chaque test.

    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);
    }

Peut-être y a-t-il une meilleure solution, mais pour l’instant, cela semble résoudre le problème, DbContext étant disposé trop tôt dans les tests.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi