如何在Mock Setup中使用泛型实现通用DB上下文单元测试方法?
解决泛型DB上下文单元测试的表属性泛型化问题
方案1:利用EF原生Set<T>()方法(推荐)
如果你的IRivetContext接口定义了DbSet<T> Set<T>() where T : class方法(和EF原生DbContext的设计一致),直接替换为调用该通用方法即可:
public static void AddDataToContext<T>(List<T> DataList, Mock<IRivetContext> context) where T : class { var dataList = DataList.AsQueryable(); var dataListMockSet = new Mock<DbSet<T>>(); dataListMockSet.As<IQueryable<T>>().Setup(x => x.Provider).Returns(dataList.Provider); dataListMockSet.As<IQueryable<T>>().Setup(x => x.Expression).Returns(dataList.Expression); dataListMockSet.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(dataList.ElementType); dataListMockSet.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(dataList.GetEnumerator()); // 替换为通用的Set<T>方法设置 context.Setup(x => x.Set<T>()).Returns(dataListMockSet.Object); }
方案2:通过表达式树动态匹配实体对应属性
如果你的IRivetContext没有定义Set<T>()方法,而是每个实体对应独立的DbSet<T>属性(比如PriceLists对应PriceList实体),可以用表达式树动态构建属性访问逻辑:
public static void AddDataToContext<T>(List<T> DataList, Mock<IRivetContext> context) where T : class { var dataList = DataList.AsQueryable(); var dataListMockSet = new Mock<DbSet<T>>(); dataListMockSet.As<IQueryable<T>>().Setup(x => x.Provider).Returns(dataList.Provider); dataListMockSet.As<IQueryable<T>>().Setup(x => x.Expression).Returns(dataList.Expression); dataListMockSet.As<IQueryable<T>>().Setup(x => x.ElementType).Returns(dataList.ElementType); dataListMockSet.As<IQueryable<T>>().Setup(x => x.GetEnumerator()).Returns(dataList.GetEnumerator()); // 动态构建访问对应DbSet属性的表达式 var contextParam = Expression.Parameter(typeof(IRivetContext), "x"); // 默认按实体名+s的规则生成属性名(如PriceList→PriceLists),规则不同可自行调整 var propertyName = typeof(T).Name + "s"; var propertyExpr = Expression.Property(contextParam, propertyName); var lambda = Expression.Lambda<Func<IRivetContext, DbSet<T>>>(propertyExpr, contextParam); context.Setup(lambda).Returns(dataListMockSet.Object); }
关键注意事项
- 若实体与上下文属性的命名规则不匹配(比如实体
Order对应属性OrderRecords),需调整propertyName的生成逻辑,或额外传入属性名作为方法参数。 - 方案1更符合EF的设计规范,后续扩展性更强,优先建议给
IRivetContext补充Set<T>()方法。
内容的提问来源于stack exchange,提问作者Jeremy D
相关产品推荐
相关产品推荐

