The current behavior of rspec-mocks causes a NoMethodError when you
call object.foo(x) after setting up a stub using
object.stub(:foo).with(y). Here’s an example for when this has caused
me a problem:
- In a before(:each) block, I’ve setup a stub:
- The spec calls some library code that calls File.exist?
- When this is called, a NoMethodError is raised due to the current
way rspec-mocks works.
David and I have been discussing this over on the issue tracker for
rspec-mocks. I originally suggested that the stub should delegate
to the original method definition when it is called with a different
argument. David brought up that this has the potential to cause
problems for people that use stubbing to prevent something destructive
form occurring. Previously, object.stub(:foo).with(x) prevented the
real definition of foo from ever being called. A NoMethodError would
be raised when a different argument is passed, but you have the
guarantee that the real, potentially destructive, definition of foo
will never be invoked. My suggested change would allow the real
definition of foo to be called when a different argument is passed.
We’re considering adding an explicit way to allow the delegate to the
original definition (i.e. maybe another method on the fluent interface
that enables this).
I’m fine with this route, but I also feel like it’s a sub-optimal
API. Just based on the naming of the API, I would expect
limit the scope of the stub so that it is only in effect when the
arguments match, and the original method definition to get used
otherwise. If you want a guarantee that a destructive method is never
invoked, you should probably just do object.stub(:foo), and not have a
with. But going this route does have the potential to cause
problems for people due to the existing behavior.
It’d be nice to get some feedback from other rspec-users on this.
Which way should we go with this? Please comment over on the github
issue tracker (linked to below) rather than here, so we keep the
discussion all in one place.