Looks like I'm having the same issue as #383, except that the fix didn't work for me.
Specifically, I'm using the Entity Framework testing library for Moq (https://github.com/scott-xu/EntityFramework.Testing/blob/master/src/EntityFramework.Testing.Moq)
When trying to Moq a DbSet with Moq >= 4.7.58, I'm getting exceptions. Tested with Moq 4.7.58, 4.7.63 and 4.7.99. Works in Moq 4.7.49.
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.
at System.Data.Entity.Infrastructure.DbQuery`1.GetInternalQueryWithCheck(String memberName)
at System.Data.Entity.Infrastructure.DbQuery`1.System.Linq.IQueryable.get_Provider()
at System.Linq.Queryable.Where[TSource](IQueryable`1 source, Expression`1 predicate)
at Ns.Store.<GetChildrenAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Ns.StoreTests.<GetChildrenAsync_NoMatchingProduct_ReturnsEmptyArray>d__11.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
at NUnit.Framework.Internal.Commands.TestMethodCommand.RunAsyncTestMethod(TestExecutionContext context)
Hi @EricStG, it's a bit difficult to help without seeing any of your code that triggers the exception. Could you please post a minimally complete repro code or unit test that causes the error?
Hi @stakx, you can find a minimal repro at https://github.com/EricStG/TestEFMoq
@EricStG: Thanks for the repro code.
Looks like I'm having the same issue as #383, except that the fix didn't work for me.
Your issue is similar, but not identical to the one you mentioned because your repro code makes use of an extension method mock.SetupData, which is not part of Moq, but comes with Entity Framework. I'm not familiar with that extension method. I suspect that there's nothing to be done at Moq's end. This might have to be fixed in the EF testing library. I'll look into it some more and report back with my findings.
@EricStG: OK, here are two possible fixes for your failing unit test:
var mockDeclaredProductSet = new Mock<DbSet<Product>>(); var mockContext = new Mock<Context>(); mockContext.Setup(c => c.DeclaredProducts).Returns(mockDeclaredProductSet.Object); // (*) var declaredProducts = new List<Product>(); mockDeclaredProductSet.SetupData(declaredProducts); var dummy = mockContext.Object.DeclaredProducts.FirstOrDefault();
mockDeclaredProductSet before you use it in (*):var mockDeclaredProductSet = new Mock<DbSet<Product>>();
var declaredProducts = new List<Product>(); // (1)
mockDeclaredProductSet.SetupData(declaredProducts); // (2)
var mockContext = new Mock<Context>();
mockContext.Setup(c => c.DeclaredProducts).Returns(mockDeclaredProductSet.Object); // (*)
var dummy = mockContext.Object.DeclaredProducts.FirstOrDefault();
Note that the two lines of code marked (1) and (2) have been moved up, before (*).
.Returns into a lambda:var mockDeclaredProductSet = new Mock<DbSet<Product>>();
var mockContext = new Mock<Context>();
mockContext.Setup(c => c.DeclaredProducts).Returns(() => mockDeclaredProductSet.Object); // (*)
var declaredProducts = new List<Product>(); // (1)
mockDeclaredProductSet.SetupData(declaredProducts); // (2)
var dummy = mockContext.Object.DeclaredProducts.FirstOrDefault();
Note the introduction of () => in the line of code marked with (*). This effectively means that the return value gets lazily evaluated, the lines marked with (1) and (2) will end up running first.
Upon the first call to .Object on a mock, Moq generates a proxy object for that mock. At this exact moment, Moq needs to know which interfaces the proxy object should "implement", or be able to intercept. Entity Framework's DbSet<TEntity> itself implements a lot of interfaces, among them IQueryable, IQueryable<T>, IEnumerable, IEnumerable<T>. Moq will only know that the generated proxy should implement these interfaces if you provide at least one setup (e.g. via mock.As<IQueryable>().Setup(...)). If you don't have any setups, and then call mock.Object, a proxy gets generated that doesn't implement any of these interfaces, therefore you'll get a NotImplementedException.
Making sure that lines (1) and (2) run before line (*) (which is the line triggering proxy object generation) has the effect of calling .SetupData, which will perform the necessary setups on the various interfaces implemented by DbSet<TEntity>. These setups in turn tell Moq that the generated proxy should implement the interfaces required by DbSet.
(If you want more details on why order of execution matters here, check out the source code for mock.SetupData and see this PR's description.)
Please let me know whether the above resolves your issue.
@stakx That should solve the issue. Thanks for looking into this.
I spent about a half a day figuring out this. Option two fixed this for me. Thank you.
I changed
mockContext.SetupGet(c => c.Organizations).Returns(_mockOrganizations.Object);
to
mockContext.Setup(c => c.Organizations).Returns(() => _mockOrganizations.Object);
Most helpful comment
I spent about a half a day figuring out this. Option two fixed this for me. Thank you.
I changed
mockContext.SetupGet(c => c.Organizations).Returns(_mockOrganizations.Object);
to
mockContext.Setup(c => c.Organizations).Returns(() => _mockOrganizations.Object);