简体   繁体   中英

Mock result from Func with NSubstitute

I'm trying to use NSubstitute to mock the return value from a Substitute, but I cannot get the substitute to return the correct value because the method signature is using a Func.

I've seen these questions, but cannot make it work with my Func.

Mocking Action<T> with NSubstitute

Mocking out expression with NSubstitute

The interface I try to mock is this (somewhat simplyfied):

public interface IOrgTreeRepository<out T> where T : IHierarchicalUnit
{
    T FirstOrDefault(Func<T, bool> predicate);
}

I'm substituting it with NSubstitute like so:

_orgTreeRepository = Substitute.For<IOrgTreeRepository<IOrganizationUnit>>();

And then I try to change the return value like so:

_orgTreeRepository.FirstOrDefault(Arg.Is<Func<IOrganizationUnit, bool>>(x => x.Id== _itemsToUpdate[0].Id)).Returns(existingItems[0]);

But it simply returns a proxy-object instead of my defined object in existingItems .

However, thanks to the other questions I managed to get this to work, but it does not help me, since I need a specific item every time.

_orgTreeRepository.FirstOrDefault(Arg.Any<Func<IOrganizationUnit, bool>>()).Returns(existingItems[0]); // Semi-working

I guess it's treating the lambda expression as a kind of absolute reference and therefore skips it? Is there any way I can mock the return value?

As you correctly guessed, NSubstitute just uses reference equality here, so unless you have a reference to the same predicate (which is sometimes an option) then you'll have to match any call ( Arg.Any or .ReturnsForAnyArgs ) or use an approximate form of matching to check the function passed in.

An example of approximate matching:

[Test]
public void Foo() {
    var sample = new Record("abc");
    var sub = Substitute.For<IOrgTreeRepository<Record>>();
    sub.FirstOrDefault(Arg.Is<Func<Record,bool>>(f => f(sample))).Returns(sample);

    Assert.AreSame(sample, sub.FirstOrDefault(x => x.Id.StartsWith ("a")));
    Assert.AreSame(sample, sub.FirstOrDefault(x => x.Id == "abc"));
    Assert.Null(sub.FirstOrDefault(x => x.Id == "def"));
}

Here we've stubbed FirstOrDefault to return sample whenever the Func<T,bool> returns true for sample (this is using a different overload of Arg.Is which takes an expression, rather than the value of the argument passed in).

This passes the test for two different predicates, because sample satisfies both of them. It also passes the last assertion, in that it doesn't return sample for a func that checks for a different id. We can't guarantee a specific predicate was used in this case, but it might be enough. Otherwise we're stuck with reference quality on the Func.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM