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

Verify for generic methods 4.12.0 vs 4.13.0 #922

Closed
bdenisenko opened this issue Sep 6, 2019 · 2 comments
Closed

Verify for generic methods 4.12.0 vs 4.13.0 #922

bdenisenko opened this issue Sep 6, 2019 · 2 comments

Comments

@bdenisenko
Copy link

Hi,

I have a set of unit tests which start failing after upgrade from Moq 4.12.0 to 4.13.0 version.
The code which illustrates the issue:

using System;
using Moq;

namespace MoqTests
{
    class Program
    {
        static void Main(string[] args)
        {
            Mock<IInnerExecutor> mock = new Mock<IInnerExecutor>();

            mock.Setup(x => x.Do<object>(It.IsAny<object>()))
                .Returns(new {result = 1})
                .Verifiable();

            mock.Setup(x => x.Do<ExecuteResult>(It.IsAny<object>()))
                .Returns(new ExecuteResult())
                .Verifiable();

            Executor obj = new Executor(mock.Object);

            ExecuteResult result1 = obj.Execute<ExecuteResult>(new { a = 1 });
            bool result2 = obj.Execute(null);

            mock.Verify(x => x.Do<ExecuteResult>(It.IsAny<object>()), Times.Exactly(1));
            mock.Verify(x => x.Do<object>(It.IsAny<object>()), Times.Exactly(1));

            Console.ReadLine();
        }
    }

    public class ExecuteResult {}

    public interface IInnerExecutor
    {
        TResponse Do<TResponse>(object p) where TResponse : new();
    }

    public class Executor
    {
        private readonly IInnerExecutor _innerExecutor;

        public Executor(IInnerExecutor innerExecutor)
        {
            _innerExecutor = innerExecutor;
        }

        public TResponse Execute<TResponse>(object p)
            where TResponse : new()
        {
            return _innerExecutor.Do<TResponse>(p);
        }

        public bool Execute(object p)
        {
            return _innerExecutor.Do<object>(p) != null;
        }
    }
}

In version 4.13.0

mock.Verify(x => x.Do<object>(It.IsAny<object>()), Times.Exactly(1));

Finds 2 executions for the line above.

Could you please let me know if it's a bug in Moq and I need to wait for the fix or it's a wrong use from my side?

Thanks.

@stakx
Copy link
Contributor

stakx commented Sep 6, 2019

Could you please let me know if it's a bug in Moq and I need to wait for the fix or it's a wrong use from my side?

TL;DR: It's not a bug in Moq, but a functional change nonetheless. Moq likely won't go back to the old behavior, so I recommend you update your test code.

The change was introduced by #904 (as mentioned briefly in the changelog). Moq has always matched generic arguments using assignment compatibility, so it shouldn't be surprising that Execute<object>(...) will also match an invocation of Execute<ExecuteResult>(...) since ExecuteResult is assignment-compatible with object.

The surprising part is rather that what made your second Verify work previously: the fact that Moq also included the return type as part of a method signature, and return types had to match precisely. (Therefore, because typeof(ExecuteResult) != typeof(object) the 2nd verify didn't match the first invocation.) This is not how the most common .NET languages (C#, VB.NET) work: there, the return type isn't considered part of a method's signature and doesn't influence method resolution. Moq now works in a manner that's more consistent with these languages.

@bdenisenko
Copy link
Author

Thank you for the clarification.

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

No branches or pull requests

2 participants