adrift in the sea of experience

Sunday, February 28, 2010

Mocking frameworks: stubs vs mocks

I just answered another question on stackoverflow about Rhino Mocks. The issue in the question comes down to a confusion between mocks and stubs as it often does. Stubs and mocks are both test doubles (i.e. a way to quickly replace a real class implementation by one that you have full control over) but they are set up in different ways.

The distinction between mocks and stubs also exists in other mocking frameworks such as jMock for java. It is important to understand the difference if you are going to do TDD with a mocking framework which supports both approaches.

Mocks

A mock is a test double where you set up behavior by providing a sequence of expected method calls and the corresponding return values. You can think of a mock as an actor in a play who has to follow a script. The script specifies both expectations (i.e. what the other actors will say and when they'll say it) and behavior (i.e. how to respond).

In practice, such a test script is recorded by making calls on the mock. After the behavior has been set up, you must explicitly switch a mock from record mode to replay mode. At the end of the test the expectations are verified. This is how it looks in Rhino Mocks:
[Test]
public void Eat_uses_banana_provider()
{
   var mocks = new MockRepository();
   var bananaProviderMock = mocks.DynamicMock<IBananaprovider>();
   bananaProviderMock.GetBanana(); // record method call expectation
   LastCall.Return(new Banana()); // tell mock what to do
   bananaProviderMock.Replay(); // stop recording, go to replay mode
   
   // actual test
   var monkey = new Monkey(bananaProviderMock);
   monkey.Eat();

   // verify the expectations that were set up earlier
   bananaProviderMock.VerifyAll();
}
Actually, I lied: this is just one way how it can look. Unfortunately a lot of cruft has accumulated in Rhino Mocks (presumably for backwards compatibility) and it has become hard to tell which is the recommended syntax.

Stubs

A stub is a test double that, unlike a mock, has no concept of a "test script". There are no separate record and replay phases: you just set up canned answers for method calls. Setting up this behavior will not create any expectations. Instead, the stub will record how it is used so that you can make assertions separately at the end of the test. In Rhino Mocks it looks like this:
[Test]
public void Eat_uses_banana_provider()
{
   var bananaProviderStub = MockRepository.GenerateStub<IBananaprovider>();
   bananaProviderStub.Stub(x => x.GetBanana()).Return(new Banana());
   
   // actual test
   var monkey = new Monkey(bananaProviderStub);
   monkey.Eat();

   // make assertion about how the stub was used
   bananaProviderStub.AssertWasCalled(x => x.GetBanana());
}

Use stubs instead of mocks if possible

There are a few reasons to use stubs instead of mocks if possible:
  • Mocks are more powerful than stubs because they can do things like giving different answers for the first and second call. However, this will make your test much harder to understand.
  • Mocks make it hard to create tests that test only one thing. If the monkey class starts creating its own bananas after a change, then ideally only the Eat_uses_banana_provider test fails. If every monkey test starts failing you will waste time diagnosing the exact problem.
  • Mocks lead to brittle tests because they expect a certain sequence of method calls. Unless you are explicitly testing that something happens in the expected order, this is rarely what you want.
If you must use mocks, use dynamic mocks rather than strict mocks (Rhino Mocks offers both). Dynamic mocks will not throw exceptions when they receive an unexpected method call. They will return a default return value if necessary (e.g. 0 or null) and will keep on trucking.

Because of the disadvantages, Moq (a popular and simpler alternative to Rhino Mocks) doesn't even offer the record/replay approach.

3 comments:

Nachiket said...

I will have to disagree with you on some of these points.

I don't understand how mocks make test harder to read. You will have to create a stub to be able to return 'canned' answers anyway. Stubs are basically dynamic mocks with some expectations set.

"Mocks make it hard to create tests that test only one thing." - I think that's the whole point of Unit Testing. You are testing one unit of code at a time and not the whole thing.

"Mocks lead to brittle tests because they expect a certain sequence of method calls." - This is a feature of mocking. You are not simply testing input -> [backbox] -> output. You are testing the behavior of the function under test. Sometimes it is desirable to make sure that your function runs a certain sequence of method calls in some specific order. For example, factories should run a specific set of procedures (function calls) to create objects in the right way. Factories work like assembly lines, producing objects for you. I would want to make sure that this assembly line is running in the right sequence.

"If you must use mocks, use dynamic mocks rather than strict mocks (Rhino Mocks offers both)." - This depends on what you are testing. For example, if I have written a test for item.save(), which saves some item class data to the database, I would use a strict mock for any other database class dependencies that this item class has. I want to make sure that when you try to save an item you only save an item and not touch the database for anything else.
On the other hand, if this item class has a dependency on a 'logger' class. I would create a dynamic mock for it. I don't care if logging is being done anywhere in the class by using any of the logger's methods. I just want to make sure that I have a fake logger.

If this method is modified to do any other database activity, the test should fail.

Wim Coenen said...

@Nachiket: Thank you for your feedback.

1. A test with mocks is harder to understand because you need to keep the "replay script" in mind. Stubs don't have such a script. Therefore, tests with stubs are easier to understand.

2. By "testing one thing", I didn't mean "testing one unit of code". I meant "testing only one behavior of that code per test". Stubs allow you to set up a bunch of behavior, but do only one explicit assertion at the end. That's good because now there's a one-to-one relation between the tested behaviors and tests. With mocks, this is not possible because each setup of behavior is automatically also an assertion. And these implicit assertions can fail in tests which were really testing something else entirely, complicating the interpretation of test failures.

3. Indeed, sometimes you want to verify that a sequence of calls happens, and mocks with their "replay script" are ideal for that. But most of my tests have no need for this, and the need to deal with the replay script (e.g. setting up expected call counts and fixing ordering) just gets in the way.

4. If you want to white-list the allowed method calls in a test, then strict mocks are indeed the best option.

Nachiket said...

Totally agree. I think that using strict mocks, dynamic mocks or stubs is entirely depends on a particular scenario. Good post, though.