web-dev-qa-db-ja.com

MoqでIQueryableを実装するクラスのモック

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

しかし、これは私がやりたいことではありません=)

40
LeffeBrune

これは新しいことではなく、よりクリーンな方法です。また、リポジトリ自体も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);
    }
}

いくつかのテストでこれを実行する可能性が高いため、これは基本的に再利用性を提供するだけであり、各テストで意図を明確にし、混乱を最小限に抑えます。 :)

46
Rune Jacobsen

ルーンの答えは素晴らしく、同じことをする方法を考える時間を節約できました。ちょっとした落とし穴は、IQueryableでIQueryable拡張メソッドを2回呼び出すと(例:ToList())、2回目に結果が返されないことです。これは、列挙子が最後にあり、リセットする必要があるためです。 Rhinomocksを使用して、GetEnumeratorの実装を次のように変更しました。

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

誰かが時間を節約できることを願っています。

8
Rob

ルーンの答えが好きです。以下は、一般的な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);
}
6
Mark Good