[rspec-devel] Partial Mocks

Brian Takita brian.takita at gmail.com
Sun Oct 1 14:56:47 EDT 2006


On 10/1/06, David Chelimsky <dchelimsky at gmail.com> wrote:
>
> On 9/30/06, Brian Takita <brian.takita at gmail.com> wrote:
> > I added Partial Mocking, aka Class Level Mocking.
>
> COOL!
>
> >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.


I moved partial mocking into its own module and removed default support for
Object. I added support to Module.
It's more that partial mocks are needed for constants and potentially
singletons. Constants tend to be modules and classes so I figure that it
would be useful in module for the same reasons that it is useful for class.

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


The partial mocking module takes care of that.

> 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.


I think partial mocking is useful even for instances because it can reduce
the scope of unit tests and avoid lots of setup  or fixture logic. Often
times, the object being tested depends on another object's state. I think
that for unit tests, we should not be concerned about how to set up the
parent.

I've been finding myself following this pattern:

foobar_called = false
another_obj.stub!(:foobar).with {foobar_called = true; false;}
obj.something
foobar_called.should_equal true

Of course, partial mocking would make this clearer

another_obj.extend Mocks::PartialMock
another_obj.should_receive(:foobar).and_return(false)
obj.something

Obviously there are places where partial mocks can quickly confuse things.
It is probably not a good idea to partially mock the class you are testing.
If you feel the temptation to, then the class probably has too many
responsibilities. Partial mocking is also less compelling for functional
tests, but the same is true for mocks.

What are your thoughts?

>  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.


Sounds good.

> 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} ???


Yes, that works too. I added it to the example.

>
> > 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.


Your welcome and the pleasure is mine. :)
I'm having fun working on rspec.

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.


I agree.
Rspec also has pretty good mock/stub support. It can even be seperated into
its own gem.

David
> _______________________________________________
> rspec-devel mailing list
> rspec-devel at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://rubyforge.org/pipermail/rspec-devel/attachments/20061001/70b6261a/attachment-0001.html 


More information about the rspec-devel mailing list