web-dev-qa-db-fra.com

Comment simuler la méthode DbContext.Entry dans Entity Framework avec un modèle de référentiel

Parce que je veux tester mon code unitaire, j'ai implémenté le modèle de référentiel dans mon application MVC4. J'ai réussi à créer une interface contextuelle, un faux contexte et à utiliser une fausse implémentation d'un System.Data.Entity.DbSet en suivant ce code.

Malheureusement, tout comme deux affiches avant moi ( ici et ici ), je n'arrive pas à me moquer des DbContext.Entry method . J'utilise cette méthode pour mettre à jour les entrées de base de données dans mon code comme suit:

DbContext.Entry(order).State = EntityState.Modified;

Je n'ai pas trouvé de solution à ce problème, seulement des gens qui disent des choses comme:

"et quel est l'intérêt d'un test unitaire de ce code? Vous simulez la méthode Find, puis vous simulez DbEntityEntry et il n'y aura pas de vraie logique à tester."

ou pour

lisez ceci et toutes les questions liées avant de continuer. (...) Si vous souhaitez tester vos référentiels créez des tests d'intégration en parlant à la vraie base de données.

C'est bien beau, mais toujours pas de réponse à la question. J'ai lu la critique et je veux toujours cette méthode d'entrée afin que je puisse utiliser un faux contexte et utiliser des objets fictifs dans mon test unitaire. Bien sûr, j'utiliserai également des tests d'intégration, mais ils ne sont pas aussi rapides que certains tests unitaires rapides.

L'erreur que je reçois lorsque j'essaie certaines implémentations est que Error 2 'Project.Models.Order' does not contain a definition for 'State' and no extension method 'State' accepting a first argument of type '[whatever return type I use]' could be found (are you missing a using directive or an Assembly reference?)

J'espère que quelqu'un pourra m'aider à créer une fausse méthode DbContext.Entry.

39
Erwin Rooijakkers

Trouvé la réponse ici en "ajoutant un niveau d'indirection supplémentaire" on obtient:

public void SetModified(object entity)
{
    Entry(entity).State = EntityState.Modified;
}

et utilisez DbContext.SetModified(entity) dans notre contrôleur.

36
Erwin Rooijakkers

Pour contourner ce problème, j'ai ajouté une surcharge de méthode et ajouté un attribut obsolète pour voir où la méthode d'origine était appelée.

    public virtual void Entry<TEntity>(TEntity entity, Action<DbEntityEntry<TEntity>> action) where TEntity : class
    {
        action(base.Entry(entity));
    }

    [Obsolete("Use overload for unit tests.")]
    public new DbEntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class
    {
        return base.Entry(entity);

        /** or **/

        throw new ApplicationException("Use overload for unit tests.");
    }

ensuite vous pouvez DbContext.Entry(order, ent => ent.State = EntityState.Modified;

5
Dave

Un exemple de la façon d'implémenter des référentiels basés sur une interface et une unité de travail pour obtenir ce que vous recherchez:

public interface IRepository<T>
    {
        T FindSingle(Expression<Func<T, Boolean>> predicate, params Expression<Func<T, object>>[] includeExpressions);
        void ProxyGenerationOn();
        void ProxyGenerationOff();
        void Detach(T entity);
        void Add(T newEntity);
        void Modify(T entity);
        void Attach(T entity);
        void Remove(T entity);
        void SetCurrentValues(T modifiedEntity, T origEntity);
        T GetById(int id);
        T GetById(int id, bool sealOverride);
        IQueryable<T> GetAll();
        IQueryable<T> GetAll(bool sealOverride);
        IQueryable<T> GetAll(string[] EagerLoadPaths);
        IQueryable<T> Find(Expression<Func<T, Boolean>> predicate);
    }



public interface IUnitOfWork : IDisposable
    {
       //repository implementations go here
       bool SaveChanges()
     }

Remarquez comment le contexte est complètement abstrait. Vous n'avez qu'à vous en soucier dans les implémentations concrètes.

2
Maess