Search code examples
c#mockingnsubstitute

Check that number of received calls is within range with NSubstitute


Is there a way to check with NSubstitute that number of received calls falls within certain range?

I would like to do something like:

myMock.Received(r => r > 1 && r <= 5).MyMethod();

Alternatively, if I could get the exact number of received calls that would do the job as well. I am unit testing retries and timeouts and based on system load and other tests running number of retries can vary during unit test execution.


Solution

  • The NSubstitute API does not currently support this exactly (but it's a nice idea!).

    There is a hacky-ish way of doing it using the unofficial .ReceivedCalls extension:

    var calls = myMock.ReceivedCalls()
        .Count(x => x.GetMethodInfo().Name == nameof(myMock.MyMethod));
    Assert.InRange(calls, 1, 5);
    

    The better way to do this using a custom Quantity from the NSubstitute.ReceivedExtensions namespace:

    // DISCLAIMER: draft code only. Review and test before using.
    public class RangeQuantity : Quantity {
        private readonly int min;
        private readonly int maxInclusive;
        public RangeQuantity(int min, int maxInclusive) {
            // TODO: validate args, min < maxInclusive.
            this.min = min;
            this.maxInclusive = maxInclusive;
        }
        public override string Describe(string singularNoun, string pluralNoun) => 
            $"between {min} and {maxInclusive} (inclusive) {((maxInclusive == 1) ? singularNoun : pluralNoun)}";
    
        public override bool Matches<T>(IEnumerable<T> items) {
            var count = items.Count();
            return count >= min && count <= maxInclusive;
        }
    
        public override bool RequiresMoreThan<T>(IEnumerable<T> items) => items.Count() < min;
    }
    

    Then:

    myMock.Received(new RangeQuantity(3,5)).MyMethod();
    

    (Note you will need using NSubstitute.ReceivedExtensions; for this.)