Rules of Engagement For Using Mock Objects
The rules are fairly simple:
- Don't use mock objects
If you think that you need to use mocks, stop and think. Are you really testing the exact interaction and sequence of calls between a class and its collaborators? Or are you merely too lazy to write a stub? If the thought of writing a stub fills you with dread, then either you're commendably lazy but should make the effort, or your code has some underlying issues that need resolving. Resolve those issues. Then come back and whine at me.
So, you've come back and whined to me about needing to use a mock object. OK. Fine. Here are the real rules of engagement:
- For every expectation set upon a mock, you must write a corresponding test to verify that the expectation is a realistic one.
- Every time you change the behaviour of an object, revisit each and every test that mocks out this behaviour and verify that the expectations used are still valid
The reason for the first rule is that if you set an expectation that an object will return null and it instead throws an exception or returns an empty list your test using mocks will pass, but won't reflect the reality of your monstrously broken application. The reason for the second reason is the same.
Now, the astute reader will have noticed that this will introduce a huge amount of work that you may not already be doing. That's the cost of using mocks. And what about stubs? Surely these incur the same cost of maintenance too? The same rules do apply, but the fact that there's a wedge of code that must be maintained inhibits wild over-use, which can only be a Good Thing.
Posted in: /tech
Simon,
I would agree 100% with you, if it was always possible to approach the use of Mocks in a purely theoretical level.
In real world situations, Mocks often become a convenience. Saying:
someMock.stubs().method("someMethod").will(returnValue(true));
is often a better approach, in my opinion, to creating a stub every time you want to stub out a collaborating object that only serves the purpose of brain dead dependency satisfaction for the test in question. Here, we move away from the purist definition of what a Mock is, but this little method ( stubs() ) means that Mocks can, misleadingly by name, conveniently by nature, act as Stubs.
Stubs can be a headache as bad as Mocks. With the added bad aftertaste of a cluttered codebase. How many times have we looked at the implementations of an interface only to find out there's a concrete one and another four that are Stubs?
I couldn't agree more however with how the overuse of either Mocks or Stubs is the code's cry for design help.
If you can, make sure your classes can be tested using SelfShunt only. And if there's a new dependency introduced, revise the design. And extract common stubs to TestMothers, or Fixtures a la Rails.
But, for the love of God, don't create a Stub every time you need a dumb object to fill a hole in your test, because when time comes for someone to clean that code up, evil curses will be muttered...
I agree that creating a stub every time you need a dumb object to fill a hole is a dumb idea. I'd suggest that using a Mock for the same purpose is equally dumb. Reusing an existing stub is slightly better, though it goes without saying that they don't have any behaviour.
I'd rather use the actual objects that something is collaborating with if possible. That way, when someone comes to clean up my code, they won't be cursing my name as they try and understand the expectations that I've put on my mocks. Having read a lot of tests that make heavy and poor use of mock objects, my experience suggests that these are far harder to comprehend than then equivalently poorly written test using mocks.
As a side note, an addition to the rules of engagement should be that you never mock anything other than the layer with which you are interacting. Any more than that, and you're in a whole world of hurt.
Hi Simon,
I agree with your rules, and posted my suggestions for winning the war at: http://sirenian.livejournal.com/32920.html
I love mocks too much to agree with the sentiment though. ;)
I blogged about this a while ago, that the use of mock objects requires duplication. http://www.digitalcompulsion.com:5030/space/Mock+Objects+Require+Code+Duplication
Nat Pryce has written a framework (OO-Matron) in which one can write a specification of an interface and ask that specification for a mock.
Nat claims that his project solves the problem of duplication arising from mocking.
http://xspecs.sourceforge.net/oomatron.html