NSubstitute icon indicating copy to clipboard operation
NSubstitute copied to clipboard

Allow Arg.Is to take a comparer and provide some useful collection comparers

Open shift-evgeny opened this issue 8 years ago • 3 comments

I've just started using NSubstitute and quickly ran into a problem when my method expected a collection argument, as described here: http://stackoverflow.com/questions/12699066/nsubstitute-checking-received-methods-with-array-arguments

The answer in that question works, but it would be simpler if I could just pass an IComparer<T> to Arg.Is<T>, which would be used by EqualsArgumentMatcher (instead of EqualityComparer<object>.Default).

Additionally, it would be useful if NSubstitute provided some collection comparers (IComparer<IEnumerable<T>>) to use with this - at least one that checks the elements in order (like SequenceEquals), but also one that doesn't (so [1, 2] and [2, 1] compare equal). Maybe also one that checks that the distinct elements are equal (so [1, 2, 1] and [2, 1] compare equal). These collection comparers should themselves take an optional comparer (IComparer<T>) for comparing the elements.

I could then call something like myObj.Received().MyMethod(Arg.Is<List<long>>(CollectionComparer.IgnoreOrder, new[] { 1, 2 })); to check that the method received a List<long> containing values 1 and 2 in any order.

shift-evgeny avatar Jan 02 '17 15:01 shift-evgeny

Thanks for the great suggestion.

I think this should tie in to the outstanding work for custom argument matchers mentioned in #160.

dtchepak avatar Jan 02 '17 22:01 dtchepak

@shift-evgeny NSubstitute is pretty extesible. As an option, you can create an extension method with IComparer<T> parameter and use predicate feature to evaluate your args:

public static class ArgExt
{
    public static T Compare<T>(T value, IComparer<T> comparer)
    {
        return Arg.Is<T>(x => comparer.Compare(value, x) == 0);
    }
}

//usage
processor.Received().ProcessSomething(ArgExt.Compare(foos, new ArrayComparer<Foo[]>()));

alexandrnikitin avatar Jan 03 '17 10:01 alexandrnikitin

I'd also vote to not implement each potential scenario as a part of the library as scenario looks a bit specific to me. As @alexandrnikitin mentioned above, you can easily create your own helpers to achieve the desired behavior :pensive:

zvirja avatar Mar 04 '18 14:03 zvirja