使用Moq测试含EF Shadow Property的查询方法的方案咨询
问题1:是否可以Mock Shadow Property?具体如何实现?
可以实现,但不建议用Moq直接Mock GetAll 方法的返回集合,原因是:EF.Property 是EF Core专属的查询方法,只有EF Core的查询提供者(比如数据库查询提供者、内存数据库提供者)能正确解析这个方法。如果你直接Mock返回内存List<AppUser>,执行LINQ to Objects的时候会因为实体类没有定义Action属性直接抛出运行时异常。
推荐最优实现方案:用EF Core官方提供的内存数据库做测试,完全不需要Mock DbContext,步骤如下:
- 安装Nuget包
Microsoft.EntityFrameworkCore.InMemory - 单元测试初始化阶段,创建使用内存数据库的DbContext实例,确保你的DbContext配置中已经为
AppUser定义了ActionShadow Property - 构造测试用的
AppUser数据,通过DbContext的Entry API给Shadow Property赋值:
var testUser = new AppUser { Id = 1, CreatedBy = 2 /* 其他属性赋值 */ }; dbContext.Add(testUser); // 给Shadow Property赋值 dbContext.Entry(testUser).Property("Action").CurrentValue = "DELETE"; await dbContext.SaveChangesAsync();
- 直接将这个DbContext实例注入到你的业务类中,调用
GetUsersByCreator方法即可正常执行包含EF.Property的查询逻辑,覆盖所有测试场景。
如果一定要用Moq做Mock,需要对查询逻辑做抽象改造:
你可以将包含Shadow Property判断的逻辑提取为可注入的接口方法,比如:
public interface IAppUserQueryFilter { bool IsDeleted(AppUser user); } // 生产环境实现 public class EfAppUserQueryFilter : IAppUserQueryFilter { public bool IsDeleted(AppUser user) => EF.Property<string>(user, "Action") == "DELETE"; }
单元测试时直接Mock IAppUserQueryFilter的IsDeleted方法返回你需要的布尔值即可,不过这种方案需要修改生产代码,性价比低于内存数据库方案。
问题2:是否应当直接将Action字段添加到实体模型中?
如果你确认Action是纯技术字段,仅用于数据库层的存储过程同步逻辑,业务层全程不需要感知这个字段的存在,就不需要添加到实体模型中,保持Shadow Property的设置完全合理,避免无关的技术字段污染业务实体的定义。
只有当后续有较多业务逻辑需要直接读写这个字段时,再考虑将其加入实体模型即可。
小提示:你提供的建表SQL存在两处问题:
Name VARCHAR(MAX) NO NULL应为Name VARCHAR(MAX) NOT NULL- 两个外键配置错误,当前是将主键
Id引用自身Id,如果要实现CreatedBy、UpdatedBy关联创建人、修改人,应该改为:CONSTRAINT FK_APPUSER_CREATION FOREIGN KEY (CreatedBy) REFERENCES AppUser (Id), CONSTRAINT FK_APPUSER_MODIFICATION FOREIGN KEY (UpdatedBy) REFERENCES AppUser (Id)
内容的提问来源于stack exchange,提问作者CERVERA
相关产品推荐
相关产品推荐

