0

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);
ChrisBint
  • 12,773
  • 6
  • 40
  • 62
  • Have you confirmed that your `UserService` constructor is assigning the `Users` property of the parameter it's passed to the `_dbSet` field? – wablab Aug 03 '16 at 16:19
  • It works unmocked if that is what you mean. – ChrisBint Aug 03 '16 at 16:23
  • Is the `NullReferenceException` thrown from the `GetById` method or the `GetAll` method? In other words, is `_dbSet` null, or is the result of `_dbSet.AsEnumerable()` null? (Or is something else null?) – wablab Aug 03 '16 at 16:26
  • Updated, the dbSet is null. – ChrisBint Aug 03 '16 at 16:30
  • Looks like you need to create a setup for the `Set` method that returns your `mockSet`. – wablab Aug 03 '16 at 16:34
  • Thats what I thought, but the docs say that it will retrieve the dbset for if it exists in the collection. If I put a watch on the DbContext, I can see the mocked 'Users'. – ChrisBint Aug 03 '16 at 16:35
  • Which version of Moq are you using? There was a bug in previous versions that caused problems with generic methods. (here's an example, although it talks about `Verify` problems: http://stackoverflow.com/questions/15124934/verifying-generic-method-called-using-moq). I guess this only applies if you're creating a setup for `Set`, though. Also, is `Users` just `public DbSet Users { get { return _dbContext.Set(); } }`? – wablab Aug 03 '16 at 16:42
  • 4.5.16, although it does the same with Rhino Mocks. The code above shows how I am accessing the DbSet from the context. – ChrisBint Aug 03 '16 at 16:48
  • Right -- I was just wondering what the implementation of `Users` was since you mentioned that you could see the mock when inspecting that property. If you're not accessing the `DbSet` via that property, and if the property isn't referencing it via the `Set()` method, then I think it's worth a shot trying the setup on `Set()`, since that method appears to be returning null. – wablab Aug 03 '16 at 16:50
  • @wablab Your suggestion to Mock .Set worked, it is also a lot easier to implement. If you offer this up as an answer, I will accept it. – ChrisBint Aug 03 '16 at 19:42
  • I added the suggestion as an answer. Thanks, @ChrisBint! – wablab Aug 03 '16 at 19:45

2 Answers2

3

I think you need to create a setup on the Set<User>() method to return your mock.

wablab
  • 1,703
  • 13
  • 15
0
    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);
  • Strangely enough, that does not work on my machine, the dbset is still null using this code, thanks though. – ChrisBint Aug 03 '16 at 16:33
  • Also inside my context I'm using `IDbSet` instead of `DbSet` try this out, maybe it will help. – Vladyslav Kushnir Aug 03 '16 at 16:38
  • Probably this your code doesn't work because you're mocking particular property of DbContext, while you're trying to access data via `Set` try to mock this method additionally. – Vladyslav Kushnir Aug 03 '16 at 16:44
  • How do you pull out the DbSet without using .Set? – ChrisBint Aug 03 '16 at 16:49
  • I'm getting that data via property of DbContext, like `new MyDbContext().MyDbEntities.FirstOrDefault(x => x.Id == 1)` or something like that... So for your case it would be `_dbContext.Users.FirstOrDefault(x => x.Id == id)` – Vladyslav Kushnir Aug 03 '16 at 16:52
  • 1
    Ahyes, but I am passing in DbContext (baseclass), not MyDbContext to the constructor of UsersService. I even changed it to the MyDbContext.Users directly and its still null.... Will pick this up again laster. – ChrisBint Aug 03 '16 at 16:58