I am working with .NET 4.5, EF6 and I'm trying to use JustMock 2.0 to test my application.
I am trying to mock my database by mocking my DbContext subclass: CoreDataRepositoryContext.
To do it, I need to mock the member SaveChanges of DbContext and every DbSet typed properties of my class CoreDataRepositoryContext by returning a fake data collection. I also need to mock the following DbSet's members:
- Add
- Remove
- AsQueryable
I need to mock it for all instances of CoreDataRepositoryContext and DbSet
For example, I have entities of type Order in database (table Orders) I did the following to mock the table Orders:
// FakeOrders is a list of orders (List<Order>)
var mockedContext = Mock.Create<CoreDataRepositoryContext>();
// Mock works
Mock.Arrange(() => mockedContext.SaveChanges()).IgnoreInstance().DoNothing();
// Mock works
Mock.Arrange(() => mockedContext.Orders).IgnoreInstance().ReturnsCollection(FakeOrders);
// Mock works
Mock.Arrange(() => mockedContext.Orders.Add(Arg.IsAny<Order>())).IgnoreInstance().DoInstead((Order o) => FakeOrders.Add(o));
// Mock works
Mock.Arrange(() => mockedContext.Orders.Remove(Arg.IsAny<Order>())).IgnoreInstance().DoInstead((Order o) => FakeOrders.Remove(o));
// Mock DOES NOT work !
Mock.Arrange(() => mockedContext.Orders.AsQueryable()).IgnoreInstance().Returns(() => FakeOrders.AsQueryable());
mockedContext.Orders is of type DbSet< Order > and FakeOrders is of type List< Order >. Both classes implement the interface IEnumerable< Order >.
Mocking Add and Remove members work well because neither of the two methods is declared in the interface IEnumerable< T >.
On the other hand, AsQueryable is declared in this interface and defined by Queryable. So, as I mock the member using IgnoreInstance, calling AsQueryable from an instance of any class which implements IEnumerable< T > launches a never ending loop. Because IEnumerable< Order >.AsQueryable is mocked by FakeOrders.AsQueryable which is mocked by... FakeOrders.AsQueryable... infinite loop...
var query = mockedContext.Orders.AsQueryable(); // Infinite loop
query = FakeOrders.AsQueryable(); // Infinite loop
query = new List<Order>().AsQueryable(); // Infinite loop
How can I do to only mock DbSet< Order >.AsQueryable specifically, without mocking IEnumerable< Order >.AsQueryable using IgnoreInstance?
Thank you for your help :)
Ok I have finally found a workaround:
I don't arrange DbContext with
IgnoreInstance
but I arrange the constructor of myDbContext
subclass when called with a specific connection string as argument:The constructor is replaced by
GetMockContext()
which retruns a mocked context: