简体   繁体   中英

Mocking DbContext in Entity Framework 6.1

I have found a number of examples that show (apparently) a clear working example of mocking DbContext with EF 6, however, none of them seem to work for me and I am not entirely sure why.

This is my unit test code that sets up the mock;

var mockData = new List<User> { new User { Email = "my@email.com", Id = 1 } }.AsQueryable();

var mockSet = new Mock<DbSet<User>>();
    mockSet.As<IQueryable<User>>().Setup(m => m.Provider).Returns(mockData.Provider);
    mockSet.As<IQueryable<User>>().Setup(m => m.Expression).Returns(mockData.Expression);
    mockSet.As<IQueryable<User>>().Setup(m => m.ElementType).Returns(mockData.ElementType);
    mockSet.As<IQueryable<User>>().Setup(m => m.GetEnumerator()).Returns(mockData.GetEnumerator());

    var mockContext = new Mock<MyDbContext>();
    mockContext.Setup(c => c.Users).Returns(mockSet.Object);

and then the call to the service I am testing;

var service = new UsersService(mockContext.Object);

var user = service.GetById(1);

This throws a NullReferenceException as the underlying DbSet is always null. The code does the following;

In BaseClass;

public IEnumerable<T> GetAll()
{
    return _dbSet.AsEnumerable();
}

In subclass;

  public User GetById(int id)
        {
            return GetAll().FirstOrDefault(x => x.Id == id);
        }

Please note that although there are other questions on SO that appear to be related, they do not cover EF 6.

For reference, this is an MSDN article that the same code with a modification to make it compile.

https://msdn.microsoft.com/en-us/data/dn314429.aspx

EDIT:

Reducing the complexity of the UserService (its uses generics/interfaces), the code is now simply;

public User GetById(int id)
        {
            return _dbContext.Set<User>().FirstOrDefault(x => x.Id == id);
        }

If I change this further to;

   var dbSet = _dbContext.Set<User>();
        return dbSet.FirstOrDefault(x => x.Id == id);

I can clearly see that dbSet is null.

Edit 2

As per the suggestion from wablab, it appears that mock .Set resolved the problem.

Credit also to Vladyslav Kushnir for the Generic method for DbSet.

Working code for this for anyone that might need it;

 private static Mock<DbSet<T>> GetDbSetMock<T>(IEnumerable<T> items = null) where T : class
        {
            if (items == null)
            {
                items = new T[0];
            }

            var dbSetMock = new Mock<DbSet<T>>();
            var q = dbSetMock.As<IQueryable<T>>();

            q.Setup(x => x.GetEnumerator()).Returns(items.GetEnumerator);

            return dbSetMock;
        }



var mockContext = new Mock<Model1>();

var users = new List<User> { new User { Email = "my@email.com", Id = 1 } };

mockContext.Setup(x => x.Set<User>()).Returns(GetDbSetMock(users).Object);

var service = new UsersService(mockContext.Object);

var user = service.GetById(1);

我认为您需要在Set<User>()方法上创建一个设置来返回您的模拟。

    private Mock<DbSet<T>> GetDbSetMock<T>(IEnumerable<T> items = null) where T : class
    {
        if (items == null)
        {
            items = new T[0];
        }

        var dbSetMock = new Mock<DbSet<T>>();
        var q = dbSetMock.As<IQueryable<T>>();

        q.Setup(x => x.GetEnumerator()).Returns(items.GetEnumerator);

        return dbSetMock;
    }

Here is my pretty well working generic method I'm using for mocking DbSet of DbContext. The actuall call of this method is:

var contextMock = new Mock<MyContext>();
contextMock.Setup(x => x.MyDbEntities).Returns(GetDbSetMock<MyDbEntity>().Object);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM