How do I mock multiple levels of DbSet.Include lambdas?
I am using Moq to write unit tests that use Entity Framework 6 DbSet
and DbContext
. I have a cascading / tiered service method Include
and I can't figure out how to set it up for testing. The service method looks something like this:
return DataContext.Cars
.Include(p => p.Model)
.Include(p => p.Model.Make)
.Select(c => new
{
Key = c.CarId,
Value = string.Format("{0} {1} {2}", c.Model.Make.Name, c.Model.Name, c.Trim)
}
).ToArray();
I know what I need to configure Include
to return the mocking object, like this:
mockCarDbSet.Setup(m => m.Include(It.IsAny<string>())).Returns(mockCarSet.Object);
But I am getting a null reference exception from cascading .Include(p => p.Model.Make)
. How do I set up Moq to handle multiple layers Include
?
EDIT
OK, so it turns out that I cannot use It.IsAny<string>
for Include
calls that use lambdas instead of strings, so now I have two problems:
- How do I set up a layout with an Include that accepts a lambda?
- Will the tuning be several levels higher than the cascade?
source to share
So, thanks to @Old Fox reminding me that Moq won't work with static members, I found a way to do it using Microsoft Fakes. Encryption allows you to push static methods. I used Moq to set up objects Mock<DbSet>
for each of the objects:
var carData = new List<Car>{new Car{ Trim = "Whatever" }};
var mockCarSet = new Mock<DbSet<Car>>();
mockCarSet.As<IQueryable<Car>>().Setup(m => m.Provider).Returns(carData.Provider);
mockCarSet.As<IQueryable<Car>>().Setup(m => m.Expression).Returns(carData.Expression);
mockCarSet.As<IQueryable<Car>>().Setup(m => m.ElementType).Returns(carData.ElementType);
mockCarSet.As<IQueryable<Car>>().Setup(m => m.GetEnumerator()).Returns(carData.GetEnumerator);
var mockMakeSet = new Mock<DbSet<Make>>();
//do the same stuff as with Car for IQueryable Setup
var mockModelSet = new Mock<DbSet<Model>>();
//do the same stuff as with Car for IQueryable Setup
using(ShimsContext.Create())
{
//hack to return the first, since this is all mock data anyway
ShimModel.AllInstances.MakeGet = model => mockMakeSet.Object.First();
ShimCar.AllInstances.ModelGet = car => mockModelSet.Object.First();
//run the test
}
source to share
include()
- static method (extension method).
Moq
does not support static mock methods ( read this link ).
To test your code, you must set mockCarDbSet
to return IQueryable<Car>
:
var carQuery = new List<Car>
{
//add cars
}
IQueryable<Post> query = carQuery.AsQueryable();
return query
as a resultDataContext.Cars
These steps will work around the static method problem.
source to share