Nsubstitute - Testing for a Specific Linq Expression

NSubstitute - Testing for a specific linq expression

The very short answer is no, NSubstitute doesn't have anything built it to make testing specific expressions easier.

The much longer answer is that there are a few options you can try, and most of them involve avoiding direct use of LINQ in the class under test. I'm not sure if any of these are good ideas as I don't know the full context, but hopefully there will be some info here you can use. In the following examples I've eliminated the Mapper step to make the code samples a bit smaller.

First option is to make it so you can check the expression is the same reference you are expecting, which means you can no longer create it directly in your code under test. 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've dumped the expression on a static Queries class, but you could use a factory to encapsulate it better. Because you have an reference to the actual expression used you can set return values and check calls were received as normal. You can also test the expression in isolation.

Second option takes this a bit further by using a specification pattern. Say you add the following member to the IRepository interface and introduce an ISpecification:

public interface IRepository<TEntity> where TEntity : IdEntity
{
/* ...snip... */
IList<TEntity> Find(ISpecification<TEntity> query);
}

public interface ISpecification<T> { bool Matches(T item); }

You can then test it like this:

//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 query in isolation to make sure it does what you think.

Third option is to catch the argument used and test it directly. This is a bit messy but 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 will hopefully be getting a bit easier in future NSubstitute versions)

Fourth option would be to find/borrow/write/steal some code that can compare expression trees, and use NSubstitute's Arg.Is(...) that takes a predicate to compare the expression trees there.

Fifth option is to not unit test it to that degree, and just integration test using a real InvoiceRepository. Rather than worry about the mechanics of what's happening, try verifying the actual behaviour you require.

My general advice would be to look at exactly what you need to test and how you can best and most easily write those tests. Remember that both the expression and the fact that it is passed through needs to be tested somehow, and the test need not be a unit test. It may also be worth considering whether the current IRepository interface is making your life easier. You could try writing the tests you would like to have, then see what design you can drive out to support that testability.

Hope this helps.

Verify ExpressionFuncT, bool argument in a unit test with NSubstitute

Capture the expression passed to the mock and use it in the Returns to verify the expected behavior.

For example

public async Task MyTestMethod() {
//Arrange
var id = 123;
var model = new Foo() {
Id = id;
}

var repository = Substitute.For<IRepository<Foo>>();

repository.SingleOrDefaultAsync(Arg.Any<Expression<Func<Foo, bool>>())
.Returns(args => {
var expression = args.Arg<Expression<Func<Foo, bool>>(); //capture expression
Foo result = expression.Compile()(model) ? model : null; //use to verify behavior
Task.FromResult(result);
});

var myClass = new MyClass(repository);

//Act
var actual = await myClass.MyMethod(id);

//Assert
actual.Should().BeTrue();
}

How to use the expression given as an argument to generate return of Substitute

That should all be done in one expression statement while configuring the expectation on the mocked repo

//Arrange
var repo = Substitute.For<ISampleRepository>();
var samples = new List<Sample>();
samples.Add(new Sample(1)) // insert sample with id 1
samples.Add(new Sample(2)) // insert sample with id 2

repo.Get(Arg.Any<Expression<Func<Sample, bool>>>())
.Returns(arg => {
var predicate = arg.ArgAt<Expression<Func<Mitarbeiter, bool>>>(0).Compile();
return samples.Where(predicate);
});

Unit Test with Nsubstitute allways return null with Lambda expression on Repository pattern

The solution is:

[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 - Testing for a specific linq expression

What is the equivalent argument matcher for a parameter like this - ExpressionFuncTEntity, bool predicate) in NSubsitute?

I think you can use argument matchers:

Arg.Any<Expression<Func<User, bool>>>()

Substitute Any() call of IDBSet/IQueryable

Now my question is if it is possible to substitute the methods of IQueryable with NSubstitute?

Any isn't a method on IQueryable. It's an extension method - a static method which is called as if it were an instance method. So unless NSubstitute can mock out static methods somehow (like Typemock Isolator does), you're going to have problems.

A really smart LINQ-oriented mocking system could essentially provide fake information to Queryable and respond to similar expression trees when it was given them by the production code - but it would be very fragile. You'd be better off with an in-memory database if at all possible.

In general, you shouldn't rely on your production code making an exact sequence of calls if another sequence would be equivalent. For example, it would be reasonable for your production code to be written as

clients.Where(p=>p.Identifier == "Test").Any()

it's not as readable, but it's fundamentally equivalent to the call to Any with a predicate. Your test should be able what the production code achieves, not the exact steps it takes to achieve it.



Related Topics



Leave a reply



Submit