Reputation: 12904
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 = "[email protected]", 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 = "[email protected]", Id = 1 } };
mockContext.Setup(x => x.Set<User>()).Returns(GetDbSetMock(users).Object);
var service = new UsersService(mockContext.Object);
var user = service.GetById(1);
Upvotes: 0
Views: 4051
Reputation: 1743
I think you need to create a setup on the Set<User>()
method to return your mock.
Upvotes: 3
Reputation: 391
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);
Upvotes: 0