eXavier February 2016

How can I change out value in callback using Moq?

I'm trying to mock some 3rd party lib in UnitTest. It puts result data in out parameter while returns bool signaling if more data is available. I want to test that my component behaves well when there are two pages of data but can't figure out how to change data in out parameter in Setup() method. I created minimalist sample that can be run in Linqpad:

void Main()
{
    var m = new Mock<IFoo>();

    string s = "1";
    var pg = 0;

    m.Setup(o => o.Query(out s))
        .Returns(() => pg==0)
        .Callback(() => { pg++; s = "2"; });

    IFoo f = m.Object;

    string z;
    while (f.Query(out z))
    {
        z.Dump();
    }
    z.Dump();
}

public interface IFoo {
    bool Query(out string result);
}

The output is

1
1

How can I do it?

Answers


eXavier February 2016

It looks like the changing of out parameters is not supported out of the box. The best solution I found so far is based on hack - see http://stackoverflow.com/a/19598345/463041. It invokes private method using reflection. Using that hack, the working solution would look like this:

void Main()
{
    var m = new Mock<IFoo>();

    string s = "1";
    var pg = 0;

    m.Setup(p => p.Query(out s))
            .OutCallback((out string v) => v = pg==0 ? "1" : "2")
            .Returns(() => pg==0)
            .Callback(() => { pg++; s = "2"; });


    IFoo f = m.Object;

    string z;
    while (f.Query(out z))
    {
        z.Dump();
    }
    z.Dump();
}

public interface IFoo {
    bool Query(out string result);
}

public static class MoqExtensions
{
    public delegate void OutAction<TOut>(out TOut outVal);
    public delegate void OutAction<in T1,TOut>(T1 arg1, out TOut outVal);

    public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, TOut>(this ICallback<TMock, TReturn> mock, OutAction<TOut> action)
        where TMock : class
    {
        return OutCallbackInternal(mock, action);
    }

    public static IReturnsThrows<TMock, TReturn> OutCallback<TMock, TReturn, T1, TOut>(this ICallback<TMock, TReturn> mock, OutAction<T1, TOut> action)
        where TMock : class
    {
        return OutCallbackInternal(mock, action);
    }

    private static IReturnsThrows<TMock, TReturn> OutCallbackInternal<TMock, TReturn>(ICallback<TMock, TReturn> mock, object action)
        where TMock : class
    {
        mock.GetType()
            .Assembly.GetType("Moq.MethodCall")
            .InvokeMember("SetCallbackWithArguments", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, mock,
                new[] { action });
        return mock as IReturnsThrows<TM 


dee February 2016

Yes it seems that the out parameter is used just once at the very beginning and the successive calls can't change it. The return value can be modified like described here but the out parameter can't.

var outResults = new Queue<string>(new[] { "first","second","third","fourth" });
string outString = outResults.Dequeue(); 

m.Setup(o => o.Query(out outString))
    .Callback(() => 
    { 
        outString = outResults.Dequeue(); 
        outString.Dump("outString from callback");
    })
    .Returns(new Queue<bool>(new[] { true, true, false }).Dequeue);

IFoo f = m.Object;

string z;
while (f.Query(out z))
{
    z.Dump("inside while");
}
z.Dump("after while");

The outString was actually changed inside of the callback as many times as the Query method was called from while but this didn't affect the value of the out parameter which value was still 'first'.

outString from callback

second 


inside while

first 


outString from callback

third 


inside while

first 


outString from callback

fourth 


after while

first 

Post Status

Asked in February 2016
Viewed 2,061 times
Voted 4
Answered 2 times

Search




Leave an answer