Paul Michaels
Paul Michaels

Reputation: 16685

Mocking DBContext in EF 6 not working as expected

I'm writing a unit test that mocks out the IDbSet properties of the DbContext; however, I seem to be getting some strange results.

Here's the code where I mock out the data:

var myData1 = new List<MyData1>()
{
    new MyData1() { Id = 2, Test = "test" },
    new MyData1() { Id = 3, Test = "test" },
    new MyData1() { Id = 4, Test = "test" }
}.AsQueryable();

IDbSet<MyData1> myDbSet = Substitute.For<IDbSet<MyData1>>();
myDbSet.Provider.Returns(myData1.Provider);
myDbSet.Expression.Returns(myData1.Expression);
myDbSet.ElementType.Returns(myData1.ElementType);

myDbSet.GetEnumerator().Returns(myData1.GetEnumerator());

myDbContext.MyData1.Returns(myDbSet);
. . .
myDbContext.MyData2.Returns(myDbSet2);
. . .
myDbContext.MyData3.Returns(myDbSet3);

When I come to interrogate the data; for example:

using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
    var myData = myDbContext.MyData1.ToList();
}

_dbContextGenerator is simply substituted to return my test DbContext, instead of the real one:

IDbContextGenerator dbContextGenerator = Substitute.For<IDbContextGenerator>();
dbContextGenerator.GenerateDbContext().Returns(myDbContext);            

This seems to work; however, if I call the method twice; it doesn't. So:

using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
    myData = myDbContext.MyData1.ToList();
}
Assert.Equal(3, myData.Count());

Works; however:

using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
    myData = myDbContext.MyData1.ToList();
}
using (IMyDbContext myDbContext = _dbContextGenerator.GenerateDbContext())
{
    myData = myDbContext.MyData1.ToList();
}
Assert.Equal(3, myData.Count());

Does not. I get no data returned; however, if I debug the line, I can see that:

myDbContextMyData1.Provider

Contains the correct test data.

Please could someone point me in the right direction on this?

Upvotes: 3

Views: 432

Answers (1)

Nkosi
Nkosi

Reputation: 247008

The problem is

myDbSet.GetEnumerator().Returns(myData1.GetEnumerator());

Which will return the same enumerator instance every time it is called.

And since the enumerator is forward only, it will need to be reset. Calling it more than once without resetting will exhibit the described behavior of only being able to enumerate once because the pointer is at the end.

Use a delegate call back so that it is invoked every time the mock is called to return a new enumerator every time GetEnumerator() is called.

myDbSet.GetEnumerator().Returns(_ => myData1.GetEnumerator());

Now enumerating the mock multiple times should then behave as expected.

Upvotes: 1

Related Questions