Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiler error when using Arg.Is<Arg.AnyType> #757

Open
srgoapp opened this issue Nov 23, 2023 · 5 comments
Open

Compiler error when using Arg.Is<Arg.AnyType> #757

srgoapp opened this issue Nov 23, 2023 · 5 comments
Assignees

Comments

@srgoapp
Copy link

srgoapp commented Nov 23, 2023

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(Expression<Predicate>)' and 'Arg.Is(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);

@srgoapp
Copy link
Author

srgoapp commented Nov 23, 2023

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*)

@GeraldLx
Copy link

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);

@srgoapp
Copy link
Author

srgoapp commented Nov 25, 2023

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))

@scottrudy
Copy link

scottrudy commented Dec 20, 2023

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.

@304NotModified
Copy link
Contributor

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?

public static ref T Is<T>(Expression<Predicate<object>> predicate) where T : AnyType

@304NotModified 304NotModified self-assigned this Apr 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants