One Mock Per Test Considered Not Awesome
For some reason I came across a rule of thumb that says something like:
You should have only one mock per test; all other interactions must be stubbed.
That sounds like good advice but it’s not. This rule of thumb doesn’t really help writing good tests and it can easily lead to testing the wrong thing.
I’m not terribly strict with nomenclature but for this article I’ll try to stick to the generally accepted differences between mocks and stubs:
- Stubs If called during a test provide canned responses. If not called doesn’t do anything. Never break a test.
- Mocks Expect the test to interact with it in a specific way. Fails the test if the actual interaction is not what was expected.
It seems like the rule above was first published by Roy Osherove in his book The Art of Unit Testing. This article is not a review of Osherove’s book, I haven’t read it yet, just bought the ebook and read enough to understand the way he presents the rule.
I think it’s fair to summarize it like this:
In a test where you test only one thing […] there should be no more than one mock object. All other fake objects will act as stubs. Having more than one mock per test usually means you’re testing more than one thing, and this can lead to complicated or brittle tests. […] Once you’ve identified [your single mock object] you can leave the others as stubs and not worry about assertions against them.
Wrong Focus
For me, tests are much like paragraphs. If you read about how a good paragraph is structured you will often find some advice like this:
A paragraph is a piece of writing that consists of several sentences. A paragraph should always have complete, correct, and concise sentences. As well it should be easy to read and well organized. The paragraph itself should focus on one subject, theme, or central idea. […] When examining a paragraph you can always ask yourself, what is the main idea in this paragraph? If you see two ideas […] you might have to create two paragraphs
So Roy is right when he says that “testing more than one thing […] can lead to complicated or brittle tests”. For me the problem is that it focuses on the wrong way to solve this.
The one subject, theme, or central idea a test should focus on should not be object interactions. This can be the case for Integration Tests –where the focus is on how different parts of a system get along– but it not for Unit Tests.
When writing Unit Tests we should not be concerned about specifying how a class does its job or which other classes it talks to. Those lead to heavy coupling between test and implementation and this is just as bad as testing many things at once, if not worse.
A Unit Test should focus on specifying what a class does. We should focus on describing what behaviour we expect from that class. How it achieves that is not the unit test’s problem, it’s design.
As a (completely biased) example, let’s say we want to write some code that lets us post status updates to multiple social networks. Following the one-mock-per-test rule we will probably end up with something like this:
public void ShouldUpdateFacebook () { var message = "Having a beer."; var twitter = MockRepository.GenerateStub<Twitter>(); var facebook = MockRepository.GenerateMock<Facebook>(); facebook.Expect(f => f.Update(message)); var updater = new StatusUpdater(facebook,twitter); updater.Update(message); facebook.VerifyAllExpectations(); } [Test()] [ExpectedException(typeof(StatusUpdateException))] public void ShouldThrowExceptionIfFacebookUpdateFails () { var message = "Having a beer."; var twitter = MockRepository.GenerateStub<Twitter>(); var facebook = MockRepository.GenerateMock<Facebook>(); facebook.Expect(f => f.Update(message)).Throw(new FacebookException()); new StatusUpdater(facebook,twitter).Update(message); } [Test()] public void ShouldUpdateTwitter () { var message = "Having a beer."; var twitter = MockRepository.GenerateMock<Twitter>(); var facebook = MockRepository.GenerateStub<Facebook>(); twitter.Expect(t => t.Update(message)); var updater = new StatusUpdater(facebook,twitter); updater.Update(message); twitter.VerifyAllExpectations(); } [Test()] [ExpectedException(typeof(StatusUpdateException))] public void ShouldThrowExceptionIfTwitterUpdateFails () { var message = "Having a beer."; var twitter = MockRepository.GenerateMock<Twitter>(); var facebook = MockRepository.GenerateStub<Facebook>(); twitter.Expect(twit => twit.Update(message)).Throw(new TwitterException()); new StatusUpdater(facebook,twitter).Update(message); }
This test provides good code coverage. It’s hard to introduce a bug that won’t break it somehow. This is all good but let me remind you again that what we should be automating here is not tests; we should be automating specifications.
Even if the test above covers all scenarios for execution it is a terrible spec for what a class should do. It doesn’t even talk about the most important expected behaviour for this class, the fact that it must update all social networks at once.
And why it doesn’t specify this? Because it’s not easy (possible?) to do that without using more than one mock! And this is no exception, is a very common scenario for most systems.
Verifying Side-Effects isn’t the Goal
If we want to finally add a proper test that verifies if the class does what it should do (send the same status update to all systems) we have to break the one-mock-per-test rule.
The reason we have to break it is very simple: mocks verify side-effects, and having more than one side effect during a test doesn’t mean testing more than one thing. Writing good tests is about asking yourself “why should I bother?” all the time; and instead of bothering about interactions we should bother about good specifications.
I don’t use mocks a lot, and I even for stubs I spend a lot of time thinking which collaborations should be tested using a double/mock/stub and what are just part of the object. The only case when I use one is when I have to test some side effect.
Having to use too many mocks in a test is definitely a bad smell; something is funny with your code. That doesn’t mean, though, that reducing the number of mocks in a test to one is a good thing.
Using mocks and interactions as drivers for test cases you away from good specifications; always keep in mind that we should only bother about what, not how.