Archive

Posts Tagged ‘Moq’

Extending Moq’s Setup() to return multiple values

June 25th, 2010

If you regularly use TDD, then you are familiar with the concept of mocking. If you don’t use TDD, then you need to slap yourself and apologize to Bob Martin, Michael Feathers and Ron Jeffries immediately. I’ll wait here…

While I would argue you should begin your TDD journey by writing your own mocks; using a framework will certainly make like easier in many instances and allow you to focus on the tests themselves.

I was a RhinoMocks guy for a long time, but have recently converted to Moq… AND I LOVE IT!!! Moq is simple and easy to use, and doesn’t get in the way of the intent of your tests. That being said, there are certain scenarios you run into with any framework that you end up coding around, and sometimes that does get in the way of your what you trying to say with your test.

Moq allows for a pretty simple syntax when you want to set up a return value on a mocked type.

?View Code CSHARP
1
2
var mockService = new Mock<IReturnStuff>();
mockService.Setup(svc => svc.SomeMethodCall()).Returns("Hello, World!");

Pretty sweet so far, and that works 99% of the time. But… what about a scenario where we want to return different values on successive calls to that method. I can think of a lot of situations where, in the absence of formal eventing, you want your code to test some value until it meets a certain criteria. You might be polling a web service for status updates, or checking a database to see if a record has been written. Whatever the case may be, it will come up.

So, for illustration purposes, let’s make up one such example. Suppose we wanted to ask a service for a specified set of values, but the return types could be varied. Perhaps we only care about some subset of those values, and want to test that our code correctly deals with variable data.

We have our service contract:

?View Code CSHARP
1
2
3
4
public interface IReturnStuff
{
    String ReturnAString();
}

Next we have our consuming class (the subject under test):

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class UsesReturnedStuff
{
    public IReturnStuff ReturnStuff { get; set; }
    private Regex Filter { get; set; }
 
    public UsesReturnedStuff(IReturnStuff returnStuff)
    {
        ReturnStuff = returnStuff;
        Filter = new Regex("(the|quick|brown|fox|hello|world)(:Pu)?", RegexOptions.IgnoreCase);
    }
 
    public List<String> GetStuff(Int32 times)
    {
        var stuff = new List<String>();
 
        for (var i = 0; i < times; i++)
        {
            var newStuff = ReturnStuff.ReturnAString();
 
            if(Filter.IsMatch(newStuff))
                stuff.Add(newStuff);
        }
 
        return stuff;
    }
}

As you can see we have a pretty basic setup here that uses a white-list approach to building up a list. We want to write some tests to mock this behavior. If we wanted two different return values when this method is called, we might instinctively write a test like this:

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
[Test]
public void LastSetupWins()
{
var mockReturnStuff = new Mock<IReturnStuff>();
var usesStuff = new UsesReturnedStuff(mockReturnStuff.Object);
 
mockReturnStuff.Setup(svc => svc.ReturnAString()).Returns("Hello");
mockReturnStuff.Setup(svc => svc.ReturnAString()).Returns("World!");
 
var stuff = usesStuff.GetStuff(2);
 
Assert.AreEqual("Hello World!", String.Join(" ", stuff.ToArray()));
}

If you couldn’t already tell from the name of the test method, this won’t work as the last return value to be Setup wins. The output from this test will produce the text:

“World! World!”

Not so great, but the Moq documentation gives us a workaround by using a callback method. A little bit of refactoring and Voila!

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Test]
public void SetupWithCallback()
{
    var mockReturnStuff = new Mock<IReturnStuff>();
    var usesStuff = new UsesReturnedStuff(mockReturnStuff.Object);
 
    var returnValues = new[] {"Hello", "World!"};
    var numCalls = 0;
 
    mockReturnStuff.Setup(svc => svc.ReturnAString())
        .Returns(() => returnValues[numCalls])
        .Callback(() => numCalls++);
 
    var stuff = usesStuff.GetStuff(returnValues.Length);
 
    Assert.AreEqual("Hello World!", String.Join(" ", stuff.ToArray()));
}

Here we are taking advantage of anonymous methods and closures to essentially keep feeding our Mock service it’s next value. This works, but… yuck! Reading this test is not very clear, and it gets in the way of the intent.
Since we are supposed to refactor our test code too, lets move the ugly bits out into a private method, and re-write our test again.

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static Int32 SetupMany(Mock<IReturnStuff> mock, Expression<Func<IReturnStuff, String>> expression, params String[] args)
{
    var numCalls = 0;
 
    mock.Setup(expression)
        .Returns(() => args[numCalls])
        .Callback(() => numCalls++);
 
    return args.Length;
}
 
[Test]
public void SetupWithExtractedMethod()
{
    var mockReturnStuff = new Mock<IReturnStuff>();
    var usesStuff = new UsesReturnedStuff(mockReturnStuff.Object);
 
    var calls = SetupMany(mockReturnStuff,
                          svc => svc.ReturnAString(),
                          "Hello", "World!");
 
    var stuff = usesStuff.GetStuff(calls);
 
    Assert.AreEqual("Hello World!", String.Join(" ", stuff.ToArray()));
}

This is good. We haven’t lost any functionality, but the intent of the test is much clearer. There is less friction because you can read the code and understand what it is doing pretty quickly without having to “decipher” the meaning.
Now, this is all fine and good, but I am likely to run into this same scenario more than once. What I would really love to have is a SetupMany() method that was built into Moq. A method I could call directly on the mocked object. Well… starting with C# 3.0 Extension Methods give us the ability to do just that. With a little work, we should be able to create a very generic extension to give us the desired behavior.

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static class MoqExtensions
{
    public static void SetupMany<TSvc, TReturn>(this Mock<TSvc> mock,
        Expression<Func<TSvc, TReturn>> expression,
        params TReturn[] args)
        where TSvc : class
    {
        var numCalls = 0;
 
        mock.Setup(expression)
            .Returns(() => numCalls < args.Length ? args[numCalls] : args[args.Length - 1])
            .Callback(() => numCalls++);
    }
}

Ok, so I know the method signature looks pretty scary. Just don’t stare at it long enough or you will go blind! The extra bit with the Ternary operator is a bit of error checking to prevent you from running over the bounds of the array. If you call the method more times than available arguments, it will simply keep returning the last argument in the list.
Refactoring our test one last time gives us an even cleaner syntax that works with any return type. The second test merely demonstrates the “sticky” final value being returned over and over.

?View Code CSHARP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[Test]
public void SetupWithGenericExtensionMethod()
{
    var mockReturnStuff = new Mock<IReturnStuff>();
    var usesStuff = new UsesReturnedStuff(mockReturnStuff.Object);
 
    mockReturnStuff.SetupMany(svc => svc.ReturnAString(), "Hello", "World!");
 
    var stuff = usesStuff.GetStuff(2);
 
    Assert.AreEqual("Hello World!", String.Join(" ", stuff.ToArray()));
}
 
[Test]
public void SetupWithLotsOfParams()
{
    var mockReturnStuff = new Mock<IReturnStuff>();
    var usesStuff = new UsesReturnedStuff(mockReturnStuff.Object);
 
    mockReturnStuff.SetupMany(svc => svc.ReturnAString(),
                              "the","quick","brown","fox","jumps","over","the","fence","hello!");
 
    var stuff = usesStuff.GetStuff(11);
 
    Assert.AreEqual("the quick brown fox the hello! hello! hello!", String.Join(" ", stuff.ToArray()));
}

And there you have it. Extending the Moq API without having to actually go in and modify the source. Hopefully this will save you a headache or two in the future.

.NET, Technology , , ,