2

I'm mocking a DbContext and its DbSets as described here.

I'd like to create a utility method for creating my mock DbContexts, which sets up each of the context's DbSets to return an empty list by default (otherwise I get errors about null references whenever I try to query one of the DbSets). Then in unit tests where I want non-empty data in a certain DbSet, I want to call Setup again on that DbSet to supply that value than I want returned. Code is below.

I did this in a unit test and it appears to be working, but I can't find anything about how Moq handles calling Setup twice on the same property. Is doing this OK, or will it have unexpected side effects? Using the debugger, I looked into context.Setups, and calling Setup a second time on the same property adds a second object to Setups rather than overwriting the first one, which worries me.

[TestClass]
public class MyTests
{
    // Creates a new Mock<MyContext> and calls Setup on each property so
    // that they all return empty lists
    private Mock<MyContext> CreateContext()
    {
        Mock<MyContext> context = new Mock<MyContext>();

        // CreateMockDbSet is a utility method which creates a Mock<DbSet<TEntity>>
        // as described in the MSDN article listed above
        context.Setup(e => e.Customers).Returns(CreateMockDbSet<Customer>().Object);
        context.Setup(e => e.Orders).Returns(CreateMockDbSet<Order>().Object);

        return context;
    }

    [TestMethod]
    public void MyTest()
    {
        // By default, context.Customers and context.Orders will both return
        // empty DbSets
        Mock<MyContext> context = CreateContext();

        List<Order> orders = new List<Order>
        {
            new Order { Id = 1 },
            new Order { Id = 2 },
            new Order { Id = 3 }
        };

        // CreateMockDbSet creates a Mock<DbSet<Order>> which returns the orders
        // in 'orders'.  What does Moq do when I call Setup again on 'Orders'?
        context.Setup(e => e.Orders).Returns(CreateMockDbSet(orders).Object);

        // ... Do test ...
    }
}

https://msdn.microsoft.com/en-us/library/dn314429(v=vs.113).aspx

Ben Rubin
  • 6,909
  • 7
  • 35
  • 82
  • If you replace `context.Setup(e => e.Customers).Returns(CreateMockDbSet().Object)` with a member field containing `CreateMockDbSet().Object` and just return that value instead of creating new dataset on every call, will it help? – Alex Seleznyov Feb 05 '18 at 15:13
  • I'm not sure what you mean? I'm not creating new databases right now; I'm just configuring my mock `DbContext` to return specific data in each test. – Ben Rubin Feb 05 '18 at 15:17
  • that was a typo. I meant dataset, not database. – Alex Seleznyov Feb 05 '18 at 15:22
  • I want to use a brand new `DbContext` and `DbSet`s within each unit test so that each test is completely self encapsulated. – Ben Rubin Feb 05 '18 at 15:26

1 Answers1

2

If you create a subsequent setup on a method and it's non-conditional (no constraints on the arguments) then it removes all previous setups for the method.

In your case where you don't have a method accepting arguments then you obviously can't add any constraints on them, so your subsequent setup will just replace the previous.

See this answer for an explanation of the source code.

Owen Pauling
  • 11,349
  • 20
  • 53
  • 64