8

I have a typical Repository Pattern setup in my application with a DbContext (EF6):

public class MyDbContext : EFContext<MyDbContext> {

    public MyDbContext () { }

    public virtual DbSet<CartItem> Cart { get; set; }

and a repository:

public class GenericEFRepository<TEntity, TContext>
    where TEntity : class, new()
    where TContext : EFContext<TContext> {

    private readonly TContext _context;

    public GenericEFRepository(TContext context) {
        _context = context;
    }
    //...
    public virtual TEntity Insert(TEntity item) {
        return _context.Set<TEntity>().Add(item);
    }

I'm testing this with Moq 4.2 (following this tutorial) by creating a mock context:

        // Arrange
        var mockSet = new Mock<DbSet<CartItem>>();

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

        // Act
        var service = new GenericEFRepository<CartItem, MyDbContext>(mockContext.Object);
        service.Insert(new CartItem() {
            Id = 1,
            Date = DateTime.Now,
            UserId = 1,
            Detail = string.Empty
        });

        // Assert
        mockSet.Verify(s => s.Add(It.IsAny<CartItem>()), Times.Once());

The problem is that when I reach this line:

return _context.Set<TEntity>().Add(item);

_context.Set<TEntity>() returns null. After some googling it seems in EF5 it was necessary to return IDbSet<T> for Moq to mock the set, but not with EF6. Is this not the case, or am I missing something?

Stephen Collins
  • 3,523
  • 8
  • 40
  • 61
  • 1
    Am I missing where you set up the `Set()` method to return the `mockSet`? – Patrick Quirk Aug 29 '14 at 13:34
  • You'll have to clarify that. According to the tutorial I linked to, the only dealings with the set are the lines `var mockSet = new Mock>();` and `mockContext.Setup(c => c.Cart).Returns(mockSet.Object);`. – Stephen Collins Aug 29 '14 at 13:36
  • 3
    That's true, but no where in the tutorial's service code (after a quick skim) do I see them calling `Set()`, while yours does. Add a setup for that and I think it will work. – Patrick Quirk Aug 29 '14 at 13:38
  • Looks like you're right! I guess I assumed since `Cart` and `.Set()` were referring to the same thing that setting up `Cart` was sufficient. Thanks! – Stephen Collins Aug 29 '14 at 13:41

2 Answers2

17

Add a setup for the Set<T>() method:

mockContext.Setup(c => c.Set<CartItem>()).Returns(mockSet.Object);

Even though on the real EFContext the property Cart and Set<CartItem>() refer to the same object, the mock of the context doesn't know that, so you need to tell it explicitly what to return.

Since it was a loose mock, the call to a method that hasn't been setup returns the default value, which in this case is null. Strict mocks are nice in helping find this error, but also have maintenance costs that other folks don't want to deal with.

Community
  • 1
  • 1
Patrick Quirk
  • 23,334
  • 2
  • 57
  • 88
  • Also might need to mock out other functions on the DbSet. – Jess Jun 03 '15 at 20:36
  • @Patrick It does not work for me. EF6 and the code is mostly the same. But I do the test on my service which access to my context via repository. So what is the solution? – Kay Aug 17 '18 at 07:30
  • You will be surprised but i didn't setup mockContext with DbSet and all works fine. But in code I need use property name as in example - property Cart. if I use with Set then it coundn't initialize DbSet. – Aliaksandr Naidzenka Oct 30 '18 at 13:01
0

This solution is still correct for me at the end of 2020 with EntitiFramework Core. Not so easy to understand how to mock objects/datasets, I'm starting right now to implement some Integration tests using in-memory DB with mocked data. I saw my method worked correctly for example if I do:

await _dbcontext.MyEntirySet.ToListAsync();

but failed when using equivalend in a generic Repository

_dbcontext.Set<TEntity>  : this return a null dataset.

I can confirm mocking Set fix the problem even with EntityFramework Core.

 _dbContextMock.Setup(c => c.Set<MyEntityType>()).Returns(mock.Object);
sam81
  • 21
  • 2