How to fake DbContext.Entry method in Entity Framework with repository pattern

21,517

Solution 1

Found the answer here by "adding additional level of indirection" we get:

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

and use DbContext.SetModified(entity) in our controller. mockContext.Setup(c => c.SetModified(It.IsAny()));

Solution 2

To get around this I added a method overload, and added an obsolete attribute to see where the original method was being called.

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

then you can DbContext.Entry(order, ent => ent.State = EntityState.Modified;

Solution 3

An example of how to implement Interface based repositories and unit of work to get what you are after:

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

Notice how the context is completely abstracted away. You only need to worry about it in the concrete implementations.

Solution 4

You could use DbContext.Update(entity). In my case, it works fine because I mocked DbContext.

_dbContext.Update(entity);
Share:
21,517

Related videos on Youtube

user2609980
Author by

user2609980

Updated on July 09, 2022

Comments

  • user2609980
    user2609980 almost 2 years

    Because I want to unit test my code I have implemented the repository pattern in my MVC4 application. I managed to make a Context Interface, a fake Context and use a fake implementation of a System.Data.Entity.DbSet by following this code.

    Unfortunately, just like two posters before me (here and here), I do not manage to mock the DbContext.Entry method. I use this method for updating database entries in my code as follows:

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

    I have not found a solution to this problem, only people who say things like:

    "and what is the point of unit testing this code? You fake the Find method, then you fake DbEntityEntry and there will be no real logic to test."

    or to

    read this and all linked questions before you continue. (...) If you want to test your repositories create integration tests talking to the real database.

    That is all good and well but still no answer to the question. I read the critique and I still do want this Entry method so I will be able to use a fake context and use mock objects in my unit test. Of course I will use integration tests as well but they are not nearly as fast as some quick unit tests.

    The error I receive when I try some implementations is that 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?)

    I hope someone can help me make a fake DbContext.Entry method.

    • Maess
      Maess over 10 years
      Use a repository pattern where the repositories implement a generic interface coupled with the unit of work pattern. In this way, you only need to mock or fake the unit of work.
    • Keith Payne
      Keith Payne over 10 years
      I looked at the first of the two SO posts that you linked to and there is an answer that was overlooked. But it might be completely irrelevant to your problem. Please post your code so that I can provide a good answer.
    • user2609980
      user2609980 over 10 years
      @KeithPayne there is an answer?
    • user2609980
      user2609980 over 10 years
      I found this answer.
    • Beau Trepp
      Beau Trepp over 9 years
      I'm using the repository pattern, but I want to test my repository!
  • CodeCaster
    CodeCaster over 10 years
    To elaborate on this: OP should, in his Modify() implementation, call DbContext.Entry(entity).State = EntityState.Modified;. Then when testing the code using the repository, you only have to mock the repository and verify that repository.Modify() was called.
  • user2609980
    user2609980 over 10 years
    @CodeCaster that is a nice explanation.
  • user2609980
    user2609980 over 10 years
    @Maess I am quite new to this, are unit of works just method calls that are abstracted away? E.g., instead of using DbContext.SaveChanges directly I do repository.SaveChanges and the context is in the repository I instantiated?
  • Maess
    Maess over 10 years
    You would call SaveChanges on your unit of work, which would in turn call SaveChanges on your context. This pattern is typically used with some for of IOC.
  • ihebiheb
    ihebiheb almost 4 years
    this is more a comment than an answer
  • LeBleu
    LeBleu almost 3 years
    Per the tags, this is not an [entity-framework-core] question, and that method does not appear to exist in EF5.