Jul 30, 2010

Mocking Comparison – Part 9: Functions

Let’s say you’ve got a mock object and you want a method on your mock to do something with a particular method that goes beyond just returning a specified value.  What do you do?  Why, you use a function of course!  When you stub a method call on a mock you simply tell the mock object to call a method or use a lambda to calculate the return value rather than just returning a specified value.

Let’s have a look at how Rhino Mocks, Moq and NSubstitute do this.

What we’re going to do is supply some simple methods that our mock object will use when we call certain methods so that we can dynamically alter the return value from the CurrentFleaCount method and we’ll then assert that the value has changed.

Rhino Mocks

[Fact]
public void Rhino_functions_and_callbacks()
{
var fleaCount = 25;
var monkey = MockRepository.GenerateMock<IMonkey>();

monkey.Stub(m => m.CurrentFleaCount()).Do(new Func<int>(() => fleaCount));
monkey.Stub(m => m.TryAddFleas(1)).IgnoreArguments()
.Do(new Func<int, bool>(fleas =>
{
fleaCount = fleas;
return true;
}));

Assert.Equal(25, monkey.CurrentFleaCount());

monkey.TryAddFleas(10);
Assert.Equal(10, monkey.CurrentFleaCount());
}

Since this is a reasonable chunk of code, let’s just look at the key parts.  When we stub the CurrentFleaCount method call we tell it to use a function that takes no arguments, and use the value of the fleaCount variable (defined in the test) to figure out what returns.

You may ask why didn’t we just do a .Return(fleaCount) like we normally would? Simple answer: we want to get the value to change as the test progresses.  By using a Func<T> means that the return value is calculated each time we call the stubbed method, rather than just having a single fixed value returned for every call.

Similarly in the stubbed TryAddFleas() method, we are providing a function that takes one parameter and returns a boolean.  Our stub method simply changes the value of fleaCount variable so that the next CurrentFleaCount() call will return a different value, and this is what the Asserts are checking for.

Hopefully that makes sense.  Form a day to day usage viewpoint it’s not something you do that often but when you need the functionality it’s really useful.

Moq

[Fact]
public void Moq_functions_and_callbacks()
{
var fleaCount = 25;
var monkey = new Mock<IMonkey>();

monkey.Setup(m => m.CurrentFleaCount()).Returns(() => fleaCount);
monkey.Setup(m => m.TryAddFleas(It.IsAny<int>()))
.Returns<int>(fleas =>
{
fleaCount = fleas;
return true;
});

Assert.Equal(25, monkey.Object.CurrentFleaCount());

monkey.Object.TryAddFleas(10);
Assert.Equal(10, monkey.Object.CurrentFleaCount());
}

The Moq syntax is much the same as the Rhino syntax, as you can see.  It’s probably a little nicer in that you use the same Returns() method to either return fixed values or call a function, unlike Rhino Mocks where you have to use the .Do() method.

It also doesn’t require the creation of new Func<T> objects – you just supply the method body you wish to use, which means less code.

NSubstitute

[Fact]
public void Nsubstitute_functions_and_callbacks()
{
var fleaCount = 25;
var monkey = Substitute.For<IMonkey>();

monkey.CurrentFleaCount().Returns(args => fleaCount);
monkey.TryAddFleas(Arg.Any<int>())
.Returns(args =>
{
fleaCount = (int)args[0];
return true;
});

Assert.Equal(25, monkey.CurrentFleaCount());

monkey.TryAddFleas(10);
Assert.Equal(10, monkey.CurrentFleaCount());
}

The NSubstitute code is a little different to the others in that instead of having parameter lists to deal with you are passed a single array containing all the parameters and it’s up to you to pull out the ones you wish to use and cast them as required by your test.

This has it’s advantages in that you avoid code littered with arguments you never use, but it has a downside in that you need to cast every parameter you do use.  That said, just like Moq, you get to use the same Returns() method for either returning a fixed value or supplying a method and you don’t need to create new Func<T> objects so the code isn’t noisy.  And better than Moq is it’s overall cleaner syntax, as we have seen in many of the previous posts.

My preference? NSubstitute.  The casting of parameters is a little annoying, but it doesn’t have Moq .Object. tax and this is one of the few times you’ll actually see a lambda in a test using NSubstitute.

 

Other posts in this series:

3 comments:

  1. In NSubstitute you can also use
    .Returns(args =>
    {
    fleaCount = args.Arg();
    return true;
    }
    The .Arg() call will return the argument of type int. This is useful when you have several arguments of different types. It will continue to work even if you change the order of arguments.

    ReplyDelete
  2. The Returns extension of NSubstitute takes a Func<CallInfo, T>, and CallInfo has a helper method to save you a cast:

    monkey.TryAddFleas(Arg.Any<int>())
    .Returns(x =>
    {
    fleaCount = x.Arg<int>();
    return true;
    });

    This works when you only have one argument of that type in the call, otherwise it will throw with an ambiguous match (in which case you can fall back on ye olde index and cast approach).

    We've considered adding an index to it, but not sure how we can do it without being confusing (is Arg(2) the 3rd int arg, or the 3rd arg cast as an int?).

    Hope this helps.
    David

    ReplyDelete
  3. @David Thanks for the clarification :-)

    ReplyDelete