非常に短い答えはノーです。NSubstituteには、特定の式のテストを容易にするためのビルドはありません。
はるかに長い答えは、試すことができるいくつかのオプションがあり、それらのほとんどは、テスト対象のクラスでLINQを直接使用しないようにすることです。完全なコンテキストがわからないため、これらのいずれかが優れたアイデアであるかどうかはわかりませんが、ここで使用できる情報があることを願っています。次の例では、コードサンプルを少し小さくするためにマッパーステップを削除しました。
最初のオプションは、式が期待しているものと同じ参照であることを確認できるようにすることです。つまり、テスト対象のコードで式を直接作成することはできなくなります。例えば:
//Class under test uses:
_invoiceRepository.Find(Queries.UnprocessedConfirmedOrders)
[Test]
public void TestUnprocessedInvoices()
{
IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
_invoiceRepository.Find(Queries.UnprocessedConfirmedOrders).Returns(expectedResults);
Assert.That(_sut.GetUnprocessedInvoices(), Is.SameAs(expectedResults));
}
式を静的クエリクラスにダンプしましたが、ファクトリを使用してより適切にカプセル化することができます。使用される実際の式への参照があるため、戻り値を設定し、呼び出しが通常どおり受信されたことを確認できます。式を単独でテストすることもできます。
2番目のオプションは、仕様パターンを使用してこれを少し進めます。次のメンバーをIRepositoryインターフェースに追加し、ISpecificationを導入するとします。
public interface IRepository<TEntity> where TEntity : IdEntity
{
/* ...snip... */
IList<TEntity> Find(ISpecification<TEntity> query);
}
public interface ISpecification<T> { bool Matches(T item); }
次に、次のようにテストできます。
//Class under test now uses:
_invoiceRepository.Find(new UnprocessedConfirmedOrdersQuery());
[Test]
public void TestUnprocessedInvoicesUsingSpecification()
{
IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
_invoiceRepository.Find(Arg.Any<UnprocessedConfirmedOrdersQuery>()).Returns(expectedResults);
Assert.That(_sut.GetUnprocessedInvoices(), Is.SameAs(expectedResults));
}
繰り返しになりますが、このクエリを個別にテストして、思ったとおりに動作することを確認できます。
3番目のオプションは、使用された引数をキャッチして直接テストすることです。これは少し厄介ですが、機能します:
[Test]
public void TestUnprocessedInvoicesByCatchingExpression()
{
Expression<Func<InvoiceDTO, bool>> queryUsed = null;
IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>();
_invoiceRepository
.Find(i => true)
.ReturnsForAnyArgs(x =>
{
queryUsed = (Expression<Func<InvoiceDTO, bool>>)x[0];
return expectedResults;
});
Assert.That(_sut.GetUnprocessedInvoices(), Is.SameAs(expectedResults));
AssertQueryPassesFor(queryUsed, new InvoiceDTO { IsProcessed = false, IsConfirmed = true });
AssertQueryFailsFor(queryUsed, new InvoiceDTO { IsProcessed = true, IsConfirmed = true });
}
(これは、将来のNSubstituteバージョンで少し簡単になることを願っています)
4番目のオプションは、式ツリーを比較できるコードを検索/借用/書き込み/盗用し、述語を使用してそこで式ツリーを比較するNSubstituteのArg.Is(...)を使用することです。
5番目のオプションは、その程度まで単体テストを行わず、実際のInvoiceRepositoryを使用して統合テストを行うことです。何が起こっているのかを心配するのではなく、必要な実際の動作を確認してみてください。
私の一般的なアドバイスは、テストする必要があるものを正確に調べ、それらのテストを最も簡単に作成する方法を検討することです。式とそれが通過するという事実の両方を何らかの方法でテストする必要があり、テストは単体テストである必要はないことを忘れないでください。また、現在のIRepositoryインターフェイスがあなたの生活を楽にしているかどうかを検討する価値があるかもしれません。必要なテストを作成してみて、そのテスト容易性をサポートするためにどのような設計を実行できるかを確認できます。
お役に立てれば。