web-dev-qa-db-ja.com

Moq.Mock <T>-式を受け取るメソッドを設定する方法

リポジトリインターフェイスをモックしているのですが、式を受け取ってオブジェクトを返すメソッドを設定する方法がわかりませんか?私はMoqとNUnitを使用しています。

インターフェース:

public interface IReadOnlyRepository : IDisposable
{
    IQueryable<T> All<T>() where T : class;
    T Single<T>(Expression<Func<T, bool>> expression) where T : class;
}

IQueryableを使用したテストはすでに設定されていますが、Tシングルの設定方法がわかりません。

private Moq.Mock<IReadOnlyRepository> _mockRepos;
private AdminController _controller;
[SetUp]
public void SetUp()
{
    var allPages = new List<Page>();
    for (var i = 0; i < 10; i++)
    {
        allPages.Add(new Page { Id = i, Title = "Page Title " + i, Slug = "Page-Title-" + i, Content = "Page " + i + " on page content." });
    }
    _mockRepos = new Moq.Mock<IReadOnlyRepository>();
    _mockRepos.Setup(x => x.All<Page>()).Returns(allPages.AsQueryable());
    //Not sure what to do here???
    _mockRepos.Setup(x => x.Single<Page>()
    //----
    _controller = new AdminController(_mockRepos.Object);
}
30
Paul

次のように設定できます。

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))//.Returns etc...;

しかし、あなたはMoqの欠点の1つに直面しています。 It.IsAnyを使用する代わりに、実際の式をそこに配置することをお勧めしますが、Moqは、特定の式を持つ式を受け取るメソッドの設定をサポートしていません(実装が難しい機能です)。難しいのは、2つの式が同等かどうかを判断する必要があるためです。

したがって、テストでは、anyExpression<Func<Page,bool>>を渡すことができ、モックを返すように設定したものはすべて返されます。テストの値は少し薄められています。

41
Jason Punyon

.Returns呼び出しで、allPages変数に対する式の結果を返すようにします。

_mockRepos.Setup(x => x.Single<Page>(It.IsAny<Expression<Func<Page, bool>>>()))
    .Returns( (Expression<Func<Page, bool>> predicate) => allPages.Where(predicate) );
8
wllmsaccnt

より正確な結果を得るには、It.Is<T>の代わりにIt.IsAny<T>を使用する必要があることがわかりました。

Page expectedPage = new Page {Id = 12, Title = "Some Title"};
_mockRepos.Setup(x => x.Single<Page>(It.Is<Expression<Func<Page, bool>>>(u => u.Compile().Invoke(expectedPage))))
                       .Returns(() => expectedPage);
4
Paul

MoqのIt.IsAny<>.CallBackなしで使用すると、テストでカバーされていないコードを書く必要があります。代わりに、クエリ/式がすべて通過できるようにし、単体テストの観点からモックを基本的に役に立たないものにします。

解決策:コールバックを使用して式をテストする必要がありますORモックをより適切に制約する必要があります。どちらの方法も面倒で困難です。私はこの問題に長い間取り組んできました。私はTDDを実践してきましたが、最終的にヘルパークラスをまとめて、これをより表現力豊かにし、煩雑さを軽減しました。考えられる最終結果の例を次に示します。

mockPeopleRepository
  .Setup(x => x.Find(ThatHas.AnExpressionFor<Person>()
    .ThatMatches(correctPerson)
    .And().ThatDoesNotMatch(deletedPerson)
    .Build()))
  .Returns(_expectedListOfPeople); 

これについて説明し、ソースコードを提供するブログ記事は次のとおりです。 http://awkwardcoder.com/2013/04/24/constraining-mocks-with-expression-arguments/

1