问题描述
我正在尝试使用最小订量(moq)模拟我们的数据库。 这里的问题是,EntityFramework中的某些方法返回DbSet对象,这些对象在我的代码中使用foreach进行了迭代。
现在我致电VerifyNoOtherCalls();在定义的测试方法的末尾。但是这里的测试使我大吃一惊,因为foreach循环从IEnumerable接口调用了GetEnumerator(),我没有验证。但这是不可能的,因为DbSet本身不实现GetEnumerator(),并且moq不允许在Lambda中调用ToList()。
现在我有两个问题:
- DbSet如何实现IEnumerable接口,而不直接或在基类中实现GetEnumerator方法?
- 如何运行测试?我已经在互联网上找到了一些说明,所以我决定自己模拟DbSet:
public static DbSet<T> GetQueryableMockDbSet<T>(List<T> sourceList) where T : class
{
var queryable = sourceList.AsQueryable();
var dbSet = new Mock<DbSet<T>>();
dbSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(queryable.Provider);
dbSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(queryable.Expression);
dbSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(queryable.ElementType);
dbSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(() => queryable.GetEnumerator());
dbSet.Setup(d => d.Add(It.IsAny<T>())).Callback<T>((s) => sourceList.Add(s));
return dbSet.Object;
}
但是,这也不起作用。
解决方法
第一个问题,DbSet<>
has an explicit implementation for IQueryable.GetEnumerator()
。这样就可以将DbSet用作IEnumerable<>
,而不会造成混乱的智能感知,并且通常不会直接在DbSet上调用这些方法。
我不太肯定为什么您发布的代码无法正常工作,但是您可以尝试在GetEnumerator()
上模拟dbSet.AsIEnumerable<T>()
。
不过,在更高的层次上,我发现在嘲笑 DbSet中没有什么价值(像 service 一样对待它,您需要模拟和验证其方法) 。我将重点放在伪造上(将其像一个集合一样处理,可以指定其内容)。因此,根据我的经验,致电VerifyNoOtherCalls()
可能无济于事。