[rspec-users] Specifying mocks

David Chelimsky dchelimsky at gmail.com
Fri May 29 07:10:42 EDT 2009

Hi Tom,

On Fri, May 29, 2009 at 4:11 AM, Tom Stuart <tom at experthuman.com> wrote:
> Hi,
> One of the problems with mocks, as far as I can tell, is that they might go
> out of sync with the real object they're mocking.

Have you read "Mock Roles, not Objects"?


> Is it possible and sane to
> detect this by running each spec against its corresponding mock? Does anyone
> already do this?

"it's corresponding mock" suggests a single mock object for each real
implementation. There's nothing stopping you from writing your own
mock objects to play this role. IMO, this is not what a dynamic mock
objects framework is for.

> For example: your Account object has a particular behavioural specification;
> you perhaps have some convenient helper method for creating a mock Account
> which conforms to that specification with various stubbed responses; and the
> specifications of other objects use that mock Account when describing
> behaviours which involve interaction with Accounts. What happens when you
> want to change the behaviour of Account? Naively, you update the
> specification and implementation of Account, your specs all pass, and you
> think you're done, except you're not: the Account mock is now
> misrepresenting the behaviour of Account all over the system, with the
> result that you've got failures that the specs don't reveal.

First of all, let's make a distinction between behaviour and method
signatures. Just because two objects sport the same methods doesn't
mean they behave the same way. In order to verify that a mock Account
honors the same contract as a real Account, you'd have to have some
very general specs like "Account balance should be an instance of the
Money class." This would not really get you very far in terms of
specifying the real account, and it's exactly the opposite of what
we're getting at with BDD - focus on what an object does (behaviour),
not what it is (structure).

So really, what we're talking about is concern over method signatures
straying. Theoretically, this could be solved with some audit
mechanism, but then we're tying mocks to specific objects. What about
when we use mocks in their most powerful way, to specify an object's
contract with polymorphic collaborators?

Consider the case of an Account in a banking application. Checking
accounts and savings accounts have some significant differences in
terms of their behaviour, yet a Transfer will probably only use the
parts that are common to both:

def transfer(source_account, target_account, amount)

an admittedly naive implementation, but it demonstrates the point. The
source account may be a checking account. It may be a savings account.
It may be a checking account with overdraw privileges. It may be a
checking account with overdraw privileges that draw on the target
account. Etc, etc. What mocks/stubs let us do here is set up the
context for each example exactly how we want it to be, with objects
that respond how we program them to, but aren't necessarily of a
specific class.

> Of course you have integration tests that will show these failures at a
> higher level but it doesn't feel fundamentally like a problem that RSpec has
> no business solving.

My personal opinion is that RSpec has no business solving this :)

Personal opinion aside, let's say we set out to solve this. We'd need
some auditing mechanism that says the object being mocked has all the
same APIs as the mock object. For RSpec, or any other Ruby framework,
we have a serious obstacle to being able to do this universally and


We're dealing with a language that lets us modify an object's
behaviour on the fly. This means that it is entirely possible (and
quite common) for a given method not to exist after a class definition
is loaded, but to be added later in the process via a dynamic method
definition or a mixin. If the point at which we do the audit, that
method has not yet been added to the object, we're screwed.

Would that be a problem all the time? Certainly not. But it means we'd
be depending on something that is not very reliable and/or restricting
our use of Ruby's most powerful features.

> Why not establish a base case for RSpec's inductive
> demonstration of correctness by running the Account spec against the
> standard Account mock? That way you'll automatically be told, by RSpec,
> whenever the actual behaviour of your mock doesn't match the specified
> behaviour of the mocked object, and you can let your integration tests
> concentrate on the hard stuff (stateful interactions between many objects)
> instead of checking that you haven't forgotten to keep your specs
> up-to-date.
> This assumes a priori that it makes engineering sense to have a
> globally-available DRY mock helper for each object (i.e. class) rather than
> building up your mocks piecemeal in every spec; I've found that I always
> start off a project by doing the latter but end up refactoring into the
> former once I get sick of mocking the same stuff over and over again. Maybe
> that's the wrong way to do things altogether, or maybe this is a process
> issue that I've misunderstood (i.e. the answer is always to use integration
> tests to detect this kind of failure), so stop me if I'm being stupid.

The most powerful use of mocks is as a design tool, allowing you to
focus on the object at hand and invent its collaborators as you go,
without having to go out and develop them just yet. As you suggest
above, even in your own process, you tend to build up mocks piecemeal
and then refactor towards a single, global helper to create a mock
Account (for example). This lets you keep focus on the task at hand,
and refactor to eliminate duplication as the duplication appears.
Seems like a perfectly viable approach to me.

Of course that doesn't solve the auditing problem I describe above,
and it also pushes you towards a one to one mapping between mocked
object and mock object, which reduces the power of mocks in terms of

So that's my 1.8 cents (recession, and all). Looking forward to some
other opinions.


> Cheers,
> -Tom

More information about the rspec-users mailing list