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

Parameter types are ignored when matching an invoked generic method against setups #903

Closed
stakx opened this issue Aug 24, 2019 · 1 comment · Fixed by #904
Closed

Parameter types are ignored when matching an invoked generic method against setups #903

stakx opened this issue Aug 24, 2019 · 1 comment · Fixed by #904
Assignees
Labels
Milestone

Comments

@stakx
Copy link
Contributor

stakx commented Aug 24, 2019

Code to reproduce:

public interface IX
{
	void M<T>(Exception arg);
	void M<T>(Uri arg);
}

[Fact]
public void Test()
{
    var exceptionSetupInvoked = false;
    var mock = new Mock<IX>();
    mock.Setup(m => m.M<object>((Exception)null)).Callback(() => exceptionSetupInvoked = true);
    mock.Setup(m => m.M<object>((Uri)null)).Callback(() => throw new Exception("Wrong setup matched."));

    mock.Object.M<int>((Exception)null);

    Assert.True(exceptionSetupInvoked);
}

Expected outcome:

The test should succeed.

Actual outcome:

The test throws the "Wrong setup matched." exception.

Further information:

While the concrete generic arguments in the above test are irrelevant (since Moq matches generic arguments by assignment compatibility), the presence of generic type parameters are central to this issue as they disable any parameter type checks that would otherwise happen. Note the else in the following:

https://github.com/moq/moq4/blob/3868424bac43b2c08342b363eb6c511fdbc982ea/src/Moq/InvocationShape.cs#L113-L126

As a side note, setup order also matters for this test.

@stakx stakx added the bug label Aug 24, 2019
@stakx stakx added this to the 4.13.1 milestone Aug 24, 2019
@stakx stakx self-assigned this Aug 24, 2019
@stakx
Copy link
Contributor Author

stakx commented Aug 24, 2019

Here are the test cases to be added to the regression test suite.
public class Issue903
{
    public interface IX
    {
        void Method<T>(bool arg);
        void Method<T>(int arg);
    }

    private readonly Mock<IX> mock;

    public Issue903()
    {
        this.mock = new Mock<IX>();
    }

    [Fact]
    public void Bool_method_was_setup_first__when_bool_method_invoked__bool_method_setup_should_be_matched()
    {
        var boolMethodInvoked = false;
        this.SetupBoolMethod(() => boolMethodInvoked = true);
        this.SetupIntMethod(() => throw new Exception("Wrong method called."));

        this.InvokeBoolMethod();

        Assert.True(boolMethodInvoked);
    }

    [Fact]
    public void Bool_method_was_setup_last__when_bool_method_invoked__bool_method_setup_should_be_matched()
    {
        var boolMethodInvoked = false;
        this.SetupIntMethod(() => throw new Exception("Wrong method called."));
        this.SetupBoolMethod(() => boolMethodInvoked = true);

        this.InvokeBoolMethod();

        Assert.True(boolMethodInvoked);
    }

    [Fact]
    public void Int_method_was_setup_last__when_int_method_invoked__int_method_setup_should_be_matched()
    {
        bool intMethodInvoked = false;
        this.SetupBoolMethod(() => throw new Exception("Wrong method called."));
        this.SetupIntMethod(() => intMethodInvoked = true);

        this.InvokeIntMethod();

        Assert.True(intMethodInvoked);
    }

    [Fact]
    public void Int_method_was_setup_first__when_int_method_invoked__int_method_setup_should_be_matched()
    {
        bool intMethodInvoked = false;
        this.SetupIntMethod(() => intMethodInvoked = true);
        this.SetupBoolMethod(() => throw new Exception("Wrong method called."));

        this.InvokeIntMethod();

        Assert.True(intMethodInvoked);
    }

    private void InvokeBoolMethod()
    {
        this.mock.Object.Method<bool>(default(bool));
    }

    private void InvokeIntMethod()
    {
        this.mock.Object.Method<int>(default(int));
    }

    private void SetupBoolMethod(Action callback)
    {
        mock.Setup(m => m.Method<object>((bool)It.IsAny<object>())).Callback(callback);
    }

    private void SetupIntMethod(Action callback)
    {
        this.mock.Setup(m => m.Method<object>((int)It.IsAny<object>())).Callback(callback);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant