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

Update from 4.9.0 to upper version breaks Setup out-param method #960

Closed
DmitriyMaximov opened this issue Nov 18, 2019 · 2 comments
Closed

Comments

@DmitriyMaximov
Copy link

DmitriyMaximov commented Nov 18, 2019

We have an interface:

public interface ITokenProvider
    {
        bool ValidateToken(string token, out Dictionary<string, string> claims);
    }

We have also a test with same code on setup (test constructor):

Mock<ITokenProvider> tokenProvider = new Mock<ITokenProvider>();
tokenProvider
    .Setup(c => c.ValidateToken(It.IsAny<string>(), out It.Ref<Dictionary<string, string>>.IsAny))
    .OutCallback((string token, out Dictionary<string, string> dic) =>
                {
                    dic = new Dictionary<string, string> {{"uid", token}};
                })
.Returns(true);

Everything was fine with Moq 3.9.0.
Sometime ago we updated to last version 4.13.1 and our setup was broken with stacktrace:

System.NullReferenceException : Object reference not set to an instance of an object.
   at Moq.MoqExtensions.OutCallbackInternal[TMock,TReturn](ICallback'2 mock, Object action)
   at Moq.MoqExtensions.OutCallback[TMock,TReturn,T1,TOut](ICallback'2 mock, OutAction'2 action)
   at Test..ctor()

When I saw to OutCallBackInternal method then I figured out that problem is in GetDeclaredMethod("SetCallbackWithArguments"), which returns null in this reflected code:

private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(
      ICallback<TMock, TReturn> mock,
      object action)
      where TMock : class
    {
      ((IFluentInterface) mock).GetType().GetTypeInfo().Assembly.GetType("Moq.MethodCall").GetTypeInfo().GetDeclaredMethod("SetCallbackWithArguments").Invoke((object) mock, new object[1]
      {
        action
      });
      return mock as IReturnsThrows<TMock, TReturn>;
    }

Same thing happened also with nearest upper version: 4.10.0.

Is I do anything wrong?

@DmitriyMaximov
Copy link
Author

Answer is here https://stackoverflow.com/a/19598345/7408879

@stakx
Copy link
Contributor

stakx commented Nov 18, 2019

Is I do anything wrong?

If you choose to reflect over Moq internals, you're basically on your own, there's no guarantee that internals stay the same across versions... they're internals, after all, and not a public API.

That being said, assuming that OutCallback does the same thing as Callback, for specifically for callbacks having certain out parameters, I suggest you simply use Moq 4.8+'s public API:

delegate void ValidateTokenCallback(string token, out Dictionary<string, string> dic);
...
tokenProvider
    .Setup(c => c.ValidateToken(It.IsAny<string>(), out It.Ref<Dictionary<string, string>>.IsAny))
    .Callback(new ValidateTokenCallback((string token, out Dictionary<string, string> dic) =>
              {
                  dic = new Dictionary<string, string> {{"uid", token}};
              }));

@devlooped devlooped locked and limited conversation to collaborators Sep 5, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants