[Rspec-devel] define_instance_method, stub_with, and mock_with

aslak hellesoy aslak.hellesoy at gmail.com
Sun Aug 27 18:24:09 EDT 2006


On 8/28/06, David Chelimsky <dchelimsky at gmail.com> wrote:
> On 8/27/06, aslak hellesoy <aslak.hellesoy at gmail.com> wrote:
> > On 8/27/06, David Chelimsky <dchelimsky at gmail.com> wrote:
> > > On 8/27/06, Brian Takita <brian.takita at gmail.com> wrote:
> > > > Hello,
> > > >
> > > > At work we came up with a trio of methods that mock out methods in an
> > > > object.
> > > >
> > > > define_instance_method
> > > > stub_with
> > > > mock_withFunny thing is around the same time, this was posted.
> > > > http://blog.seagul.co.uk/articles/2006/06/30/very-very-lightweight-mocking-ish
> > > >
> > > > Here is a snippit from that article.
> > > >
> > > > class Object
> > > >
> > > >   def metaclass
> > > >     class << self; self; end
> > > >    end
> > > >
> > > >   def define_instance_method(sym, &block)
> > > >      metaclass.__send__(:define_method, sym, &block)
> > > >   end
> > > >
> > > >   def stub_instance_method(sym, &block)
> > > >      raise "#{self} does not respond to <#{sym}> and therefore cannot be
> > > > stubbed" unless self.respond_to?(sym)
> > > >      define_instance_method(sym, &block)
> > > >   end
> > > >
> > > >   def __log__
> > > >     @__log__ ||= []
> > > >   end
> > > >
> > > >  end
> > > >
> > > > So you can use this like:
> > > > o = Object.new
> > > >  o.define_instance_method(:foo) do
> > > >   :bar
> > > >  end
> > > > o.foo # return :bar
> > > >
> > > > We went a little further by adding the mock_with and stub_with methods,
> > > > which take a Hash and allow you to mock out the method names corresponding
> > > > to the keys.
> > > >
> > > > For example,
> > > > o = Object.new
> > > > two_value = 2
> > > >  o.stub_with(:one => 1, :two => proc {two_value})
> > > > o.one # returns 1
> > > > o.two # returns 2
> > > >
> > > >  o = Object.new
> > > > mock_action = mock("mock_action")
> > > >  mock_action.should_receive(:something).with(:grey_poupon)
> > > > o.mock_with (:receive => proc {mock_action)}
> > > > o.receive.something(:grey_poupon)
> > > >
> > > > David, I also remember you where working on acts_as_mock. This has helped us
> > > > with our tests and I think it would also make using specs more convenient
> > > > and easier to read.
> > > > What does everybody think? If it sounds good, I'll create a patch.
> > >
> > > I started working on acts_as_mock because rails forces us to use
> > > static methods to find objects, which couples us to the database in
> > > our controller specs:
> > >
> > > Person.find(123)
> > >
> > > In retrospect, all we really need is the ability to mock class level
> > > methods and then return a mock:
> > >
> > > specify "should display person when show is requested" do
> > >   person = mock("person")
> > >   Person.should_receive(:find).with(123).and_return(person)
> > >   person.should_receive(:name).and_return("Joe")
> > >   get 'show'
> > > end
> > >
> > > specify "should ask Person for a new one when create is requested" do
> > >   person = mock("person")
> > >   Person.should_receive(:new).and_return(person)
> > >   get 'create'
> > > end
> > >
> > > The code for this much is already in a branch, so I'll revisit that in
> > > the next few days.
> > >
> > > As for the idea you are presenting, in general, I think that mocking
> > > methods on real instances is  a risky proposition. I've seen this
> > > result in tests that are very difficult to understand when they fail,
> > > as some methods on the instance are mocked and some are not. I also
> > > have yet to see a case where   using partial mocks is a better
> > > decision than improving the decoupling in the system under test.
> > >
> >
> > Isn't acts_as_mock (the branch experiment) using 'partial' mocking?
> > (Redefining class and instance methods on existing classes)
>
> It was originally, but I'm going to try to do it w/ only mocking the
> class methods so we can use them to return mocks rather than returning
> instances acting like mocks. I think that will be cleaner.
>

In Ruby, classes are *real* objects
(http://www.rubycentral.com/book/ref_c_class.html), so making this
available for classes should be no different than making it available
for all objects. -Unless we make it explicitly available for Class
only, which to me seems like a somewhat arbitrary limitation.

Aslak

> >
> > Aslak
> >
> > > That's just my 2 cents. Anyone else?
> > >
> > > David
> > > _______________________________________________
> > > Rspec-devel mailing list
> > > Rspec-devel at rubyforge.org
> > > http://rubyforge.org/mailman/listinfo/rspec-devel
> > >
> > _______________________________________________
> > Rspec-devel mailing list
> > Rspec-devel at rubyforge.org
> > http://rubyforge.org/mailman/listinfo/rspec-devel
> >
> _______________________________________________
> Rspec-devel mailing list
> Rspec-devel at rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-devel
>


More information about the Rspec-devel mailing list