So EntityFramework 6 is a lot better testable then previous versions. And there are some nice examples on the internet for frameworks like Moq, but the case is, I prefer using NSubstitute. I've got the "non-query" examples translated to work with the use of NSubstitute, but I can't get my head around the 'query-test'.
How does Moq's items.As<IQueryable<T>>().Setup(m => m.Provider).Returns(data.Provider);
translate to NSubstitute? I thought something like ((IQueryable<T>) items).Provider.Returns(data.Provider);
but that didn't work. I've also tried items.AsQueryable().Provider.Returns(data.Provider);
but that didn't work either.
The exeption I'm getting is:
"System.NotImplementedException : The member 'IQueryable.Provider' has not been implemented on type 'DbSet
1Proxy' which inherits from 'DbSet
1'. Test doubles for 'DbSet`1' must provide implementations of methods and properties that are used."
So let me quote the code example from the link above. This code sample uses Moq to mock the DbContext and DbSet.
public void GetAllBlogs_orders_by_name()
{
// Arrange
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var mockSet = new Mock<DbSet<Blog>>();
mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider);
mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
var mockContext = new Mock<BloggingContext>();
mockContext.Setup(c => c.Blogs).Returns(mockSet.Object);
// ...
}
And this is how far I come with NSubstitute
public void GetAllBlogs_orders_by_name()
{
// Arrange
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();
var mockSet = Substitute.For<DbSet<Blog>>();
// it's the next four lines I don't get to work
((IQueryable<Blog>) mockSet).Provider.Returns(data.Provider);
((IQueryable<Blog>) mockSet).Expression.Returns(data.Expression);
((IQueryable<Blog>) mockSet).ElementType.Returns(data.ElementType);
((IQueryable<Blog>) mockSet).GetEnumerator().Returns(data.GetEnumerator());
var mockContext = Substitute.For<BloggingContext>();
mockContext.Blogs.Returns(mockSet);
// ...
}
So the question is; How does one Substitute a property of IQueryable (like Provider)?
You shouldn't need to mock all of the pieces of the IQueryable. When I use NSubstitute for mocking an EF DbContext I do something like so:
With a simple implementation of IDbSet around a list or something for my MockDbSet().
In general you should be mocking interfaces, not types as NSubstitute will only override virtual methods.
Thanks to Kevin, I've found the problem in my code translation.
The unittest code samples are mocking
DbSet
, but NSubstitute requires the interface implementation. So the equivalent of Moqsnew Mock<DbSet<Blog>>()
for NSubstitute isSubstitute.For<IDbSet<Blog>>()
. You're not always required to provide the Interface, so that's why I was confused. But in this specific case, it turned out to be crucial.It also turned out that we don't have to cast to Queryable when using the interface IDbSet.
So the working test code:
I've written a small extention method to cleanup the Arrange section of the unit tests.
Not the question, but in case you also need to be able to support async operations:
This is my static generic static method to generate fake DbSet. It may by useful.
This happens because of NSubstitute syntax specific. For example in:
NSubstitute calls the Provider's getter, then it specifies the return value. This getter call isn't intercepted by the substitute and you get an exception. It happens because of explicit implementation of IQueryable.Provider property in DbQuery class.
You can explicitly create substitutes for multiple interfaces with NSub, and it creates a proxy which covers all specified interfaces. Then calls to the interfaces will be intercepted by the substitute. Please use the following syntax:
I wrote a wrapper about a year ago around the same code you are referencing from Testing with Your Own Test Doubles (EF6 onwards). This wrapper can be found on GitHub DbContextMockForUnitTests. The purpose of this wrapper is to reduce the amount of repetitive/duplicate code needed to setup unit tests that make use of EF where you want to mock that
DbContext
andDbSets
. Most of the mock EF code you have in the OP can reduced down to a 2 lines of code (and only 1 if you are usingDbContext.Set<T>
instead of DbSet properties) and the mock code is then called in the wrapper.To use it copy and include the files in folder
MockHelpers
to your Test project.Here is an example test using what you had above, notice that there is now only 2 Lines of code are needed to setup the mock
DbSet<T>
on the mockedDbContext
.It is just as easy to make this a test that invokes something that uses the async/await pattern like
.ToListAsync()
on theDbSet<T>
.