The shortest answer is no, NSubstitute hasn't built anything to make it easier to test specific expressions.
The much longer answer is that there are several options you can try, and most of them are related to the need to use LINQ directly in the test class. I am not sure that any of them are good ideas, because I do not know the full context, but I hope that there will be some information. In the following examples, I eliminated the Mapper step to make the code samples a little smaller.
The first option is to make it so that you can verify that the expression is the same link that you expect, which means that you can no longer create it directly in the test code. For example:
//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)); }
I dumped the expression in the Queries static class, but you can use factory to encapsulate it better. Since you have a reference to the actual expression used, you can set the return values ββand verify that the calls were received as usual. You can also check the expression in isolation.
The second option requires this a little more, using the specification template. Suppose you add the following member to the IRepository interface and make ISpecification:
public interface IRepository<TEntity> where TEntity : IdEntity { IList<TEntity> Find(ISpecification<TEntity> query); } public interface ISpecification<T> { bool Matches(T item); }
Then you can check it as follows:
//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)); }
Again, you can test this request in isolation to make sure that it does what you think.
The third option is to catch the argument used and check it directly. This is a bit dirty, but it works:
[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 }); }
(This, hopefully, will be a little easier in future versions of NSubstitute)
The fourth option would be to search / borrow / write / steal code that can compare expression trees, and use NSubstitute Arg.Is (...), which takes a predicate to compare expression trees there.
The fifth option is not to have a unit test to such an extent, but just an integration test using a real InvoiceRepository. Instead of worrying about the mechanism of what is happening, try checking the actual behavior that you require.
My general advice would be to see what you need to check, and how you can best and easiest to write these tests. Remember that both the expression and the fact that it went through should be tested in some way, and the test should not be unit test. It is also worth considering whether the current IRepository interface makes your life easier. You can try to write the tests that you would like to have, and then see what design you can output to support this testability.
Hope this helps.