如何模拟多个级别的 DbSet.Include lambdas

本文关键字:DbSet lambdas Include 何模拟 模拟 | 更新日期: 2023-09-27 17:56:24

我正在使用 Moq 编写使用实体框架 6 DbSetDbContext对象的单元测试。 我有一个具有级联/多级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才能返回模拟对象,如下所示:

mockCarDbSet.Setup(m => m.Include(It.IsAny<string>())).Returns(mockCarSet.Object);

但是我从级联.Include(p => p.Model.Make)中得到一个空引用异常. 如何设置最小起订量来处理多个级别的Include

编辑
好的,事实证明我不能将It.IsAny<string>用于使用 lambda 而不是字符串的Include调用,所以现在我有两个问题:

  1. 如何使用接受 lambda 的包含设置模拟?
  2. 上述设置会级联到多个级别吗?

如何模拟多个级别的 DbSet.Include lambdas

include()是一个静态方法(扩展方法)。 Moq不支持静态方法模拟(阅读此链接)。

要测试您的代码,您需要将mockCarDbSet设置为返回IQueryable<Car>

var carQuery = new List<Car>
{
    //add cars
}
IQueryable<Post> query = carQuery.AsQueryable();
由于

DataContext.Cars而返回query

这些步骤将解决静态方法问题。

因此,

多亏@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
}