简体   繁体   中英

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM