NSubstitute - Testing for a specific linq expression

I am using the repository template in the MVC 3 application that I am currently developing. My repository interface is as follows:

public interface IRepository<TEntity> where TEntity : IdEntity { void Add(TEntity entity); void Update(TEntity entity); void Remove(TEntity entity); TEntity GetById(int id); IList<TEntity> GetAll(); TEntity FindFirst(Expression<Func<TEntity, bool>> criteria); IList<TEntity> Find(Expression<Func<TEntity, bool>> criteria); } 

In many cases, when coding methods in my service classes, I use the FindFirst and Find methods. As you can see, they both accept the linq expression as input. I want to know if there is a way by which NSubstitute allows you to specify a specific expression that you want to test in your code.

So, here is an example of a service method that illustrates the use of one of the repository methods that I mentioned:

 public IList<InvoiceDTO> GetUnprocessedInvoices() { try { var invoices = _invoiceRepository.Find(i => !i.IsProcessed && i.IsConfirmed); var dtoInvoices = Mapper.Map<IList<Invoice>, IList<InvoiceDTO>>(invoices); return dtoInvoices; } catch (Exception ex) { throw new Exception(string.Format("Failed to get unprocessed invoices: {0}", ex.Message), ex); } } 

So, is there a way using NSubtitute that I can check for a specific lambda expression: i => !i.IsProcessed && i.IsConfirmed ?

Any guidance would be appreciated.

+9
source share
4 answers

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 { /* ...snip... */ 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.

+13
source

I came across this question when I was trying to figure out how to return a specific value using the lambda expression in NSubstitute. However, for my use case, I don't care what is actually passed to the linq query, and would like to share how to return the values ​​for linq queries on the mocking interfaces in NSubstitute.

So using the example above

 [Test] public void TestUnprocessedInvoices() { IList<InvoiceDTO> expectedResults = new List<InvoiceDTO>(); _invoiceRepository.Find(Arg.Any<Expression<Func<Invoice, bool>>>()).Returns(expectedResults); } 
+5
source

I did not want to refuse to use Expression<Func<T,bool>> in my repository interface, as an alternative to programming this particular layout (since NSubstitute did not support it), I just created a private class in my test that implemented my repository interface and only the method associated with the expression that this test will use. I was able to continue using NSubstitute to mock all other dependencies, as usual, but I could use the same repository for several different tests and actually get different results from different inputs.

 public class SomeFixture { private readonly IRepository<SomeEntity> entityRepository; private readonly IRepository<SomeThing> thingRepository; public SomeFixture() { var entities = new List<SomeEntity> { BuildEntityForThing(1), BuildEntityForThing(1), BuildEntityForThing(1), BuildEntityForThing(2), }; entityRepository = new FakeRepository(entities); thingRepository = Substitute.For<IRepository<SomeThing>>(); thingRepository.GetById(1).Returns(BuildThing(1)); thingRepository.GetById(2).Returns(BuildThing(2)); } public void SomeTest() { var classUnderTest = new SomeClass(thingRepository, entityRepository); Assert.AreEqual(classUnderTest.FetchEntitiesForThing(1).Count, 3); } private void SomeOtherTest() { var classUnderTest = new SomeClass(thingRepository, entityRepository); Assert.AreEqual(classUnderTest.FetchEntitiesForThing(2).Count, 1); } private class FakeRepository : IRepository<SomeEntity> { private readonly List<SomeEntity> items; public FakeRepository(List<SomeEntity> items) { this.items = items; } IList<TEntity> Find(Expression<Func<SomeEntity, bool>> criteria) { // For these purposes, ignore possible inconsistencies // between Linq and SQL when executing expressions return items.Where(criteria.Compile()).ToList(); } // Other unimplemented methods from IRepository ... void Add(SomeEntity entity) { throw new NotImplementedException(); } } } 
+3
source

There is a way to do this by comparing lambda expressions for equality. A very popular answer was written to the corresponding question here , which gives an example of the LambdaCompare class.

Then you can use this LambdaCompare to check for equality or lambda for equality in your dummy setup:

 var mockRepository = Substitute.For<IRepository>(); mockRepository.Find(Arg.Is<Expression<Func<Invoice, bool>>>(expr => LambdaCompare.Eq(expr, i => !i.IsProcessed && i.IsConfirmed)) .Returns(..etc..) 

Only if the dummy repository .Find() is called with the expression i => !i.IsProcessed && i.IsConfirmed , will it return what was specified in .Returns()

0
source

All Articles