Jul 23, 2010

Mocking Comparison - Part 5: Repetitions

In Part 4 we looked at how parameter constraints are handled and in Part 3 we looked at how to do interaction based tests with mocks.  In this part we bring those two pieces together and add a little extra to check if a call was made a specific number of times.

Let’s just jump straight into the code shall we?

Rhino Mocks

Our test here is simply going to call a method a number of times and verify that the call was made the correct number of times using constraints to verify the correct calls.  It’s a completely useless test, other than as a vehicle to show you how to do this sort of thing

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

monkey.TryAddFleas(5);
monkey.TryAddFleas(-1);
monkey.TryAddFleas(9);

monkey.AssertWasCalled(m => m.TryAddFleas(0),
options => options.Constraints(
Is.GreaterThan(3) && Is.LessThanOrEqual(10)
)
.Repeat.Twice());
monkey.AssertWasCalled(m => m.TryAddFleas(-1),
options => options.Repeat.Once());
}

Note the important part, the .Repeat.Twice() and .Repeat.Once() calls.  It’s these calls that define our expectations as to how many times the call should have been made.

Rhino also features a .Repeat.Time(n) call you can use as well if once or twice don’t cut it for you.

Moq

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

monkey.Object.TryAddFleas(5);
monkey.Object.TryAddFleas(-1);
monkey.Object.TryAddFleas(9);

monkey.Verify(m => m.TryAddFleas(
It.IsInRange(3,10,Range.Exclusive)
),
Times.Exactly(2));
monkey.Verify(m => m.TryAddFleas(-1), Times.Once());
}

Instead of using the word Repeat, Moq uses Times.  Apart from that there is little difference.

NSubstitute

Unfortunately NSubstitute doesn’t support this feature yet as it’s still a maturing framework.  If you really need to do this type of testing then you’ve got a few options – use Rhino or Moq, contribute to the NSubstitute project, or to just not do this type of testing :-).

Interaction based testing is valid at times, but it’s generally brittle and is usually a sign of “implementation verifying” tests rather than behaviour/specification verifying tests (but that’s an argument for another time)

 

Verdict: Moq wins out in this case simply because it’s constraint system is more terse than the Rhino one, but this is purely a personal taste thing.

 

Other posts in this series:

5 comments:

  1. Richard,

    Thank you for the excellent series! BTW, this post is the only one in the series that is not labeled with "mocking" tag, so it is not retrieved when searching for the whole series.

    ReplyDelete
  2. I'm really enjoying your series! Well done.

    As far as I am aware, you can use Arg in Rhino Mocks just like the Moq 'Is' class to specify argument constraints in line. It's a much easier syntax :)

    ReplyDelete
  3. This feature is now available in NSubstitute, see https://github.com/nsubstitute/NSubstitute/pull/63 for more details

    ReplyDelete
  4. Using Nsub's new feature, I wrote the following for your reference:

    [Test]
    public void NsubtituteRepetitions()
    {
    var monkey = Substitute.For();
    monkey.TryAddFleas(5);
    monkey.TryAddFleas(-1);
    monkey.TryAddFleas(9);
    monkey.Received(2).TryAddFleas(Arg.Is(count => count > 3 && count < 10));
    monkey.Received().TryAddFleas(Arg.Is(count => count == -1));
    }

    ReplyDelete