[mocha-developer] mocking missing methods
dan at tastapod.com
Tue May 1 18:28:56 EDT 2007
James Mead wrote:
> Wow - it's great to have so much input on this...
It's obviously something we've all been thinking about :)
> I really like "quacks_like". :-)
So that's three for three... hmm!
> I'm not clear why you would want to be able to do this if Sheep & Ninja are
> distinct classes.
Think of a class/module definition describing a role (an aspect of
behaviour). Say in your production code, you have a Shop object
providing a bunch of services. You might want to use it as a
RetailOpportunity, a DeliveryDestination, a ShopliftingVenue or any
number of other expressively-named roles. These might well be
implemented as mixins - the shop would "quack like a" delivery
destination if you were a courier - and you want to test that behaviour.
So you need a way of saying "I want to mock this module" (or more
accurately "I want to mock something in the role described by this
module"). Now either a) mocha takes the module/class name, and needs to
"construct" an instance of the module, say using Object.new.extend(mod)
or class constructor, or the user has to provide an object that includes
the module, which pushes the onus onto the user (and by implication adds
noise in the test).
In the former case, you get the examples I suggested, and mocha has to
do more work. In the latter case, mocha would take a ready-made
instance, which means the user - and therefore the test - would have to
do more stuff to express its intent.
> If the Sheep class includes Enumerable at class
> definition time, any Sheep instance will respond_to? Enumerable methods.
Right, but what if I don't care about whether or not it's a Sheep? Just
that right now it's being Enumerable?
> However, it would be nice to have a solution that covers extending a Sheep
> instance at runtime. I'm not so sure that there is anything wrong with your
> "yuk" example given that it is a bit of an edge case.
I respectfully disagree. If you are using mocking to express
interactions between roles, then this will be very much the norm.
> One thing I don't think I explained about my equivalent of
> "responds_like_type" i.e. "responds_like(instance_of(Sheep))" was that it
> would use a sneaky trick to construct an instance of the class without
> calling initialize purely in order to be able to use "respond_to?" instead
> of "instance_method"...
That'd work (unless the class defines methods or extends itself in its
constructor, in which case all bets are off anyway!). In
statically-typed languages the convention is to mock roles rather than
classes (hence the focus on interfaces as roles). In a dynamic language
things are a bit squishier. Mocking modules (with Object.new.extend(mod)
and classes (by bypassing the constructor) seems like a pretty solid
approach, and is more expressive than just mock().
My original use case was for the mock to fail with an undefined method
error if I called a method that didn't exist in the mocked type, and
this will give me that and more besides, so I'm happy :)
> I think I'd prefer to use "respond_to?" in this way in the underlying
> implementation of "responds_like_type". As I've said the "respond_to?"
> method feels like the correct method to use in all circumstances.
I agree that a well-implemented object should respond_to? the right things.
Anyway, thanks for listening. Mocha is looking great James, and I'll
happily fall in line with whatever you decide.
More information about the mocha-developer