[rspec-devel] Partial Mocks

David Chelimsky dchelimsky at gmail.com
Sun Oct 1 11:23:50 EDT 2006

On 9/30/06, Brian Takita <brian.takita at gmail.com> wrote:
> I added Partial Mocking, aka Class Level Mocking.


>For now, I added
> should_receive and should_not_receive to Object, but it can be moved to
> Class and/or its own module.
> I understand the Partial Mocking is controversial and dangerous if used
> incorrectly, but I've been finding myself using them with success.
> I'd advocate that Partial Mocking is a "sharp knife" that, if used
> correctly, can be a nice tool that simplifies tests/specs.

Where I see this as necessary (and useful) is in cases like
ActiveRecord subclasses that are designed to be used as their own
factories. In static languages, this would be viewed as a big no-no by
the TDD community, but the fact that Ruby makes it so easy to
intercept calls changes the landscape a bit.

What we *could* do, if we all agree that we prefer to encourage
partial mocking at the class level but discourage it at the instance
level, is make it a module that we include in the Class class. Then,
if a developer chooses to add it to instances of a class, he can
include it in the class in the spec file.

This way it's only automagic on classes, but still accessible on instances.

> Of course when used incorrectly, it can "slice through bone".

What is your view of correct/incorrect? Maybe we should come up w/
some guidelines for that for the rspec documentation.

>  Anyways, here is an example. It is committed inside of the stubs branch.
> How should we proceed?

Let's let it stew in the branch for a few days, but not too long. I'm
planning to do the 0.6.4 release tomorrow (sans partial mocking and
stubbing) just to get a wealth of improvements out. No reason this
can't be in a 0.6.5 release later this week.

> require File.dirname(__FILE__) + '/../lib/spec'
> class MockableClass
>   def self.find id
>     return :original_return
>   end
> end
> context "A partial mock" do
>   specify "should be able to stub objects" do
>     obj = Object.new
>     obj.should_receive(:foobar).and_return {:return_value}
>     obj.foobar.should_equal :return_value
>   end
>   specify "should work at the class level" do
>     MockableClass.should_receive(:find).and_return {:stub_return}
>     MockableClass.find(1).should_equal :stub_return
>   end
>   specify "should revert to the original after each spec" do
>     MockableClass.find(1).should_equal :original_return
>   end
> end

How about MockableClass.should_receive(:find).with("1").and_return
{:stub_return} ???

> Thanks,
> Brian

As insincere as this phrase usually sounds, I mean it sincerely: no,
thank you! This is a very important part of rspec support for rails
apps. A lot of ppl are using mocha/stubba w/ test/unit because class
level mocking/stubbing is such a great aid to testing controllers and
views. This is a real deficiency in rspec, and I think that resolving
it will open the rspec door for a lot more rails developers.

Of course, we're also going to offer a plugin seam for mocha/stubba,
flexmock, or any other framework, so people who prefer them can use
them. But none of them have the same syntax as the other rspec
expectations, so I like the idea of continuing to support these
facilities out of the box in rspec.


More information about the rspec-devel mailing list