问题
I need to mock EF's DbContext
. I use the approach here and it works well.
// mock a DbSet
var mockDbSet = Substitute.For<DbSet<Foo>, IQueryable<Foo>>();
var data = new List<Foo>().AsQueryable();
((IQueryable<Foo>)mockDbSet).Provider.Returns(data.Provider);
((IQueryable<Foo>)mockDbSet).Expression.Returns(data.Expression);
((IQueryable<Foo>)mockDbSet).ElementType.Returns(data.ElementType);
((IQueryable<Foo>)mockDbSet).GetEnumerator().Returns(data.GetEnumerator());
// now add it to a mock DbContext
var mockContext = Substitute.For<MyDbContextClass>();
mockContext.Set<Foo>().Returns(mockDbSet);
However in some tests I need to be able to call mockContext.Set<Foo>().Add(someFoo)
and mockContext.Set<Foo>().Remove(otherFoo)
, and for the underlying add/remove logic to work.
I tried this:
mockDbSet.When(x => x.Add(Arg.Any<Foo>())).Do(x => data.Add(x.Arg<Foo>()));
but it throws with Collection was modified; enumeration operation may not execute.
So how do I implement add/remove functionality?
回答1:
You don't want to add it to the collection. What you want to do is check if it (add/remove/etc) was called and possibly what it was called with.
// arrange - check what it was called with. You place asserts in the body of the `Do` expression. Place this before your call that actually executes the code
mockDbSet.Add(Arg.Do<Foo>(foo =>
{
Assert.IsNotNull(foo);
// Assert other properties of foo
}));
// act
// assert. Make sure that it was actually called
mockDbSet.Received(1).Add(Arg.Any<Foo>());
If you want to add a Foo
at a later point in your test you can keep the reference to the List
of Foo
's.
// mock a DbSet
var mockDbSet = Substitute.For<DbSet<Foo>, IQueryable<Foo>>();
var fooList = new List<Foo>();
var data = fooList.AsQueryable();
// rest of your code unchanged
// add it from the code being tested through the mock dbset
mockDbSet.Add(Arg.Do<Foo>(foo =>
{
fooList.Add(foo);
// at this point you have to recreate the added IQueryable
data = fooList.AsQueryable();
// rest of code you had to add this to the mockDbSet
}));
// act
回答2:
The comment by @Stilgar made me look into the EntityFramework.Testing library, which solves this very problem.
So I replaced my stuff with this library (one less thing to worry about).
来源:https://stackoverflow.com/questions/39488256/how-do-i-mock-dbcontext-using-nsubstitute-and-then-add-remove-data