NSubstitute
NSubstitute copied to clipboard
Compiler error when using Arg.Is<Arg.AnyType>
Describe the bug When using Arg.Is<Arg.AnyType>(expression), depending on the expression, the code can't compile. The compiler error is Error CS0121 : The call is ambiguous between the following methods or properties: 'Arg.Is<T>(Expression<Predicate<T>>)' and 'Arg.Is<T>(Expression<Predicate
To Reproduce
public interface ISomethingWithGenerics
{
void SomeAction<TState>(int level, TState state);
string SomeFunction<TState>(int level, TState state);
void SomeActionWithGenericConstraints<TState>(int level, TState state) where TState : IEnumerable<int>;
string SomeFunctionWithGenericConstraints<TState>(int level, TState state) where TState : IEnumerable<int>;
}
[Fact]
public void Is_matcher_works_with_AnyType()
{
ISomethingWithGenerics something = Substitute.For<ISomethingWithGenerics>();
something.SomeFunction(Arg.Any<int>(), Arg.Any<Arg.AnyType>()).Returns("matched");
var result = something.SomeFunction(7, 3409);
Assert.Equal("matched", result);
something.Received(1).SomeFunction(Arg.Any<int>(), Arg.Is<Arg.AnyType>(state => state.ToString() == 3409.ToString()));
}
The lambda expression: state => (int)state == 3409) is legal
The lambda expression: state => state.ToString() == 3409.ToString() produces the compile error:
NotificationServiceTest.cs(315, 64): [CS0121] The call is ambiguous between the following methods or properties: 'Arg.Is<T>(Expression<Predicate<T>>)' and 'Arg.Is<T>(Expression<Predicate<object>>)'
Expected behaviour No compiler error
Environment:
- NSubstitute version: 5.1.0
- Platform: .Net 7 project (windows)
Additional context Trying to migrate away from Moq.
Func<object, Type?, bool> state = (v, t) => v.ToString()!.Contains(expectedMessage);
logger.Verify(
x => x.Log(
It.Is<LogLevel>(l => l == expectedLogLevel),
It.IsAny<EventId>(),
It.Is<It.IsAnyType>((v, t) => state(v, t)),
It.IsAny<Exception>(),
It.Is<Func<It.IsAnyType, Exception?, string>>((v, t) => true)), (Times)times);
It's possible to remove the compile error by changing
something.Received(1).SomeFunction(Arg.Any<int>(), Arg.Is<Arg.AnyType>(state => state.ToString() == 3409.ToString()));
to
Func<Arg.AnyType, bool> match = v => v.ToString() == 3409.ToString();
something.Received(1).SomeFunction(Arg.Any<int>(), Arg.Is<Arg.AnyType>(state => match(state)));
but no match will be found.
Expected to receive exactly 1 call matching:
SomeFunction<Arg+AnyType>(any Int32, state => Invoke(value(UnitTests.Notifications.NotificationServiceTest+<>c__DisplayClass15_0).match, state))
Actually received no matching calls.
Received 1 non-matching call (non-matching arguments indicated with '*' characters):
SomeFunction<Int32>(7, *3409*)
Not sure what you are trying to do.
The Arg.AnyType was introduced to say "I dont care which type, I just want to test that the method was called". So the combination of Arg.Is with Arg.AnyType doesnt make sense for me.
You want to check that SomeFunction was called with the integer 3409, so just write:
something.Received(1)
.SomeFunction(Arg.Any<int>(), Arg.Is<int>(3409));
which can be shortened to:
something.Received(1)
.SomeFunction(Arg.Any<int>(), 3409);
Arg.Is with Arg.AnyType is for testing the generic parameter. If i would like to mock the ILogger method and assert that the logger was called with a log message that contained a specific message.
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter);
Arg.Is<Arg.AnyType>(state => state.ToString().Contains(expectedMessage))
I ran into this exact same issue while converting from Moq, except I was using StartsWith(expectedMessage). However, the message I see from the compiler is CS1660 Cannot convert the lambda expression to type 'Arg.AnyType' because it is not a delegate type.
The Arg.AnyType was introduced to say "I dont care which type, I just want to test that the method was called". So the combination of Arg.Is with Arg.AnyType doesnt make sense for me.
Funny, as there is some case in the code for this?
https://github.com/nsubstitute/NSubstitute/blob/1f2fb0f05dd5ae581d0467e963dc81c96ddac261/src/NSubstitute/Arg.cs#L56