Using Moq to mock a method with out parameters


In my home projects I came across the need to write a unit test for a method with the following signature:

bool TryGetSubstitution(string word, out string substitution)

Mocking this using Moq is pretty straightforward:

_mockSub = "subword";
_testOrthoepedia = new Mock<IOrthoepedia>(MockBehavior.Strict);
_testOrthoepedia
    .Setup(to => to.TryGetSubstitution(It.IsAny<string>(), out _mockSub))
    .Returns(true);

But the problem with this is that, while it allows the TryGetSubstitution() method to be called, it essentially does nothing! Prior to the call _mockSub is set to "subword" and after the call _mockSub is set to "subword". No change, no variation. I needed Moq to return different values for the out parameter in different calls.

Easy, you say: in each test, before the call is made, just change the value of _mockSub to the desired return value.

_mockSub = "subword";
_testOrthoepedia = new Mock<IOrthoepedia>(MockBehavior.Strict);
_testOrthoepedia
    .Setup(to => to.TryGetSubstitution(It.IsAny<string>(), out _mockSub))
    .Returns(true);
_var testObj = _testOrthodepia.Object;
...
_mocksub = "wordA"; 
var result = testObj.TryGetSubstitution("wordA", out _mocksub);
// _mocksub is set to "wordA"
...
_mocksub = "wordB"; 
var result = testObj.TryGetSubstitution("wordB", out _mocksub); 
// _mocksub is set to "wordB"

Wrong! When the .Setup() is called at runtime, the mock is created using the current value of _mocksub, which will be “subword“. So when each call to TryGetSubstituion() is made, the mock will return the value it has associated with the out parameter: “subword“. After each call in the above code _mocksub is set to "subword", not "wordA" or "wordB". This behaviour could be used that to verify that TryGetSubstitution() has been called, but not to get different values back in your unit tests.

I looked online for a solution, but I found the answers both confusing, and obsolete. Among them was this one by Scott Wegner on this StackOverflow page. The code he provided looked promising and I tried it out, but it did not work. The call to InvokeMember() kept failing because SetCallbackWithArguments() no longer exists in the Moq assembly. That was disappointing.

Then, after returning to the page at least 20 times, I finally noticed the header above the code (For Moq version(sic) before 4.10), and above that he has code for later versions of Moq, like I am using (yes, I can be dense at times).

To try out this variation, I added a definition for a new delegate and changed my mock. In this case, I changed the mock so that calls to TryGetSubstitution() would return whatever value is in the first parameter:

public delegate void CallbackDelegate(string p1, out string p2);
...
_mockSub = "subword";
_testOrthoepedia = new Mock<IOrthoepedia>(MockBehavior.Strict);
_testOrthoepedia
    .Setup(to => to.TryGetSubstitution(It.IsAny<string>(), out _mockSub))
    .Callback(new CallbackDelegate((string word, out string substitution) => substitution = word))
    .Returns(true);
_var testObj = _testOrthodepia.Object;
...
var result = testObj.TryGetSubstitution("wordA", out _mocksub);
// _mocksub is set to "wordA" 
...
var result = testObj.TryGetSubstitution("wordB", out _mocksub);
// _mocksub is set to "wordB"

… and lo and behold: each time I called TryGetSubstitution() _mockSub was changed to the value of the first parameter passed in the call.

To genericize and expand upon this in the future I have set up a new MoqDelegates class

namespace CDP.Common.Utilities.Delegates
{
    public static class MoqDelegates
    {
        public delegate void OutAction<TOut>(out TOut outVal);
        public delegate void OutAction<T1, TOut>(T1 arg1, out TOut outVal);
        public delegate void OutAction<T1, T2, TOut>(T1 arg1, T2 agr2, out TOut outVal);
    }
}

to which additional signatures can be added as needed. And to use it, I now just add the following set up to my tests’ mocks:

outVarType outVar = initialvalue;
ClassToMock
    .Setup(to => to.MethodToMock(p1Type, p2Type,..., out outVar))
    .Callback(new OutAction<p1Type, p2Type,..., outVarType>((p1Type p1, p2Type p2,..., outVarType pN) => {callback code to set out parameters}))

e.g.

string _mockSub = "subword";
_testOrthoepedia = new Mock<IOrthoepedia>(MockBehavior.Strict);
_testOrthoepedia
    .Setup(to => to.TryGetSubstitution(It.IsAny<string>(), out _mockSub))
    .Callback(new OutAction<string, string>((string word, out string substitution) => { substitution = word; }))
    .Returns(true);
_var testObj = _testOrthodepia.Object;
...
var result = testObj.TryGetSubstitution("wordA", out _mocksub);
// _mocksub is set to "wordA"

var result = testObj.TryGetSubstitution("wordB", out _mocksub); 
// _mocksub is set to "wordB"

Hopefully, that will be enough for me, and perhaps someone else out there, to be able to expand upon and create whatever mock is needed for test cases involving out parameters.

Edit: Note that in the example MoqDelegates class I presented above, all the delegates are named OutAction. I have found it useful to give each a slightly different name in order to allow for possible conflicts in signatures.

e.g. Trying to create a new delegate in the MoqDelegates class where the 2nd of 3 parameters is an out parameter, like this:

public delegate void OutAction<T1, TOut, T3>(T1 arg1, TOut agr2, T3 arg3);

would conflict with the third delegate. To avoid this, the delegates could be named OutAction, InOutAction, InInOutAction, and the new one, InOutInAction.

namespace CDP.Common.Utilities.Delegates
{
    public static class MoqDelegates
    {
        public delegate void OutAction<TOut>(out TOut outVal);
        public delegate void InOutAction<T1, TOut>(T1 arg1, out TOut outVal);
        public delegate void InInOutAction<T1, T2, TOut>(T1 arg1, T2 agr2, out TOut outVal);
        public delegate void InOutInAction<T1, TOut, T3>(T1 arg1, TOut agr2, T3 arg3);
    }
}

Edit: If you saw previous versions of this post, you may notice that I have removed the in parameter modifier from the delegate declarations. I was so focused on getting this construct to work that I never noticed that the in modifiers were actually there. Since they do change the way that the methods operate, I decided it would be prudent to remove them from the examples. Users can add them back when the parameter needs to be passed by reference.

Edit: In showing how to use the MoqDelegates class, I incorrectly included the out keyword before the generic type specification of the out parameter. I have now removed it.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s