模拟使用Moq实现IQueryable的类

时间:2011-08-13 04:09:49

标签: c# unit-testing mocking moq

我花了一个晚上试图模拟一个实现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的属性/方法会更容易,并且就像这样简单地模拟:

repo.Setup(r => r.GetItems()).Returns(new Items[]{ }.AsQueryable());

但这不是我想做的事情=)

5 个答案:

答案 0 :(得分:43)

这不是什么新鲜事,只是一种更干净的方式。我也有存储库,其中存储库本身也是一个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);
    }
}

这基本上只提供了可重用性,因为您可能希望在多个测试中执行此操作,并且在每个测试中,它使意图清晰且最简单。 :)

答案 1 :(得分:7)

符文的答案很棒,让我省时间搞清楚如何做同样的事情。 小问题是,如果你在IQueryable上调用一些IQueryable扩展方法两次(例如ToList()),那么第二次你将得不到任何结果。那是因为调查员在最后并且需要重置。 使用Rhinomocks我将GetEnumerator的实现更改为:

mock.Stub(r => r.GetEnumerator()).Do((Func<IEnumerator<T>>) (() => { 
    var enumerator = queryable.GetEnumerator();
    enumerator.Reset();
    return enumerator;
}));

希望能节省一些时间。

答案 2 :(得分:6)

我喜欢Rune的回答。这是一个通用的IQueryable版本:

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);
}

答案 3 :(得分:2)

我认为这是关于你可以用Moq做的最好的事情。我认为更可读的选择是推出自FakeRepo<T>衍生的System.Linq.EnumerableQuery<T>

public class FakeRepo<T> : EnumerableQuery<T>, IRepo<T>
{
    public FakeRepo(IEnumerable<T> items) : base(items) { }
}

更新:您可以通过模拟EnumerableQuery<T>然后使用As<T>()来解决此问题:

var items = new Item[0];

var repo = new Mock<EnumerableQuery<Item>(items).As<IRepo>();

答案 4 :(得分:0)

我有同样的问题。我通过更改此行来修复它

mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator());

mock.Setup(r => r.GetEnumerator()).Returns(queryable.GetEnumerator);

我希望此处不需要其他评论。