如何模拟多个级别的 DbSet.Include lambda?
How do I mock multiple levels of DbSet.Include lambdas?
我正在使用 Moq 编写使用 Entity Framework 6 DbSet
和 DbContext
对象的单元测试。我有一个带有 cascading/multi-level Include
的服务方法,但我不知道如何设置它进行测试。服务方法看起来像这样:
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();
我知道我必须将 Include
设置为 return 模拟对象,如下所示:
mockCarDbSet.Setup(m => m.Include(It.IsAny<string>())).Returns(mockCarSet.Object);
但是我从级联 .Include(p => p.Model.Make)
中得到一个空引用异常。如何设置 Moq 以处理多个级别的 Include
?
编辑
好的,事实证明我不能将 It.IsAny<string>
用于使用 lambdas 而不是字符串的 Include
调用,所以现在我有两个问题:
- 如何使用接受 lambda 的 Include 设置模拟?
- 上面的设置会级联到多级吗?
include()
是静态方法(扩展方法)。
Moq
不支持静态方法 mock(read this link).
要测试您的代码,您需要将 mockCarDbSet
设置为 return IQueryable<Car>
:
var carQuery = new List<Car>
{
//add cars
}
IQueryable<Post> query = carQuery.AsQueryable();
return query
由于 DataContext.Cars
这些步骤将解决静态方法问题。
感谢@Old Fox 提醒我 Moq 不能与静态成员一起使用,我找到了一种使用 Microsoft Fakes 执行此操作的方法。垫片允许您填充静态方法。我使用 Moq 为每个实体设置 Mock<DbSet>
个对象:
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
}
我正在使用 Moq 编写使用 Entity Framework 6 DbSet
和 DbContext
对象的单元测试。我有一个带有 cascading/multi-level Include
的服务方法,但我不知道如何设置它进行测试。服务方法看起来像这样:
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();
我知道我必须将 Include
设置为 return 模拟对象,如下所示:
mockCarDbSet.Setup(m => m.Include(It.IsAny<string>())).Returns(mockCarSet.Object);
但是我从级联 .Include(p => p.Model.Make)
中得到一个空引用异常。如何设置 Moq 以处理多个级别的 Include
?
编辑
好的,事实证明我不能将 It.IsAny<string>
用于使用 lambdas 而不是字符串的 Include
调用,所以现在我有两个问题:
- 如何使用接受 lambda 的 Include 设置模拟?
- 上面的设置会级联到多级吗?
include()
是静态方法(扩展方法)。
Moq
不支持静态方法 mock(read this link).
要测试您的代码,您需要将 mockCarDbSet
设置为 return IQueryable<Car>
:
var carQuery = new List<Car>
{
//add cars
}
IQueryable<Post> query = carQuery.AsQueryable();
return query
由于 DataContext.Cars
这些步骤将解决静态方法问题。
感谢@Old Fox 提醒我 Moq 不能与静态成员一起使用,我找到了一种使用 Microsoft Fakes 执行此操作的方法。垫片允许您填充静态方法。我使用 Moq 为每个实体设置 Mock<DbSet>
个对象:
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
}