用Moq实现IQueryable mock类
本文关键字:mock IQueryable 实现 Moq | 更新日期: 2023-09-27 18:05:33
我花了一个晚上的时间来模拟一个实现IQueryable对象:
public interface IRepo<T> : IQueryable<T>
{
}
我能想到的最好的东西是这样的:
var items = new Item[] {}.AsQueryable();
var repo = new Mock<IRepo>();
repo.Setup(r => r.GetEnumerator()).Returns(items.GetEnumerator());
repo.Setup(r => r.Provider).Returns(items.Provider);
repo.Setup(r => r.ElementType).Returns(items.ElementType);
repo.Setup(r => r.Expression).Returns(items.Expression);
是否有更简洁的方法来做同样的事情?在IRepo中公开一个属性/方法会更容易,它返回IQueryable和简单的mock,像这样:
repo.Setup(r => r.GetItems()).Returns(new Items[]{ }.AsQueryable());
但这不是我想做的=)
这不是什么新鲜事,只是一种更简洁的方法。我还有一些储存库,其中储存库本身也是IQueryable,所以我需要同样的东西。我基本上只是把你的代码放在一个扩展方法中,就像这样,在我的测试项目的根级,使它对所有测试都可用:
public static class MockExtensions
{
public static void SetupIQueryable<T>(this Mock<T> mock, IQueryable queryable)
where T: class, IQueryable
{
mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
mock.Setup(r => r.Provider).Returns(queryable.Provider);
mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
mock.Setup(r => r.Expression).Returns(queryable.Expression);
}
}
这基本上只是提供了可重用性,因为您可能希望在几个测试中这样做,并且在每个测试中,它都使意图清晰,混乱最小化。:)
Rune的答案很棒,节省了我思考如何做同样的事情的时间。一个小问题是,如果你在IQueryable上调用一些IQueryable扩展方法两次(例如ToList()),那么第二次你将得不到任何结果。这是因为枚举数位于末尾,需要重置。使用Rhinomocks,我将GetEnumerator的实现更改为:
mock.Stub(r => r.GetEnumerator()).Do((Func<IEnumerator<T>>) (() => {
var enumerator = queryable.GetEnumerator();
enumerator.Reset();
return enumerator;
}));
希望这能为大家节省一些时间。
我喜欢Rune的答案。这是一个通用的iquerable版本:
public static void SetupIQueryable<TRepository, TEntity>(this Mock<TRepository> mock, IQueryable<TEntity> queryable)
where TRepository : class, IQueryable<TEntity>
{
mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
mock.Setup(r => r.Provider).Returns(queryable.Provider);
mock.Setup(r => r.ElementType).Returns(queryable.ElementType);
mock.Setup(r => r.Expression).Returns(queryable.Expression);
}
我认为这是Moq所能做到的最好的。我认为一个更可读的选择是滚动你自己的FakeRepo<T>
,从System.Linq.EnumerableQuery<T>
派生:
public class FakeRepo<T> : EnumerableQuery<T>, IRepo<T>
{
public FakeRepo(IEnumerable<T> items) : base(items) { }
}
Update:您可以通过模拟EnumerableQuery<T>
然后使用As<T>()
:
var items = new Item[0];
var repo = new Mock<EnumerableQuery<Item>(items).As<IRepo>();
我也有同样的问题。我通过修改
一行来修复它mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());
mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator);
我希望这里不需要额外的注释