Forum: RSpec Mocking and stubbing model relationships

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (nickh)
on 2008-11-18 06:42
(Received via mailing list)
Before writing specs for a one-to-many relationship between two
models, I did some research and found that some people were creating
proxy mocks, and others were using Matthew Heidemann's
#stub_association! (which essentially does that for, but in a nice,
DRY way):
   http://www.ruby-forum.com/topic/126993

Are those two methods currently the accepted "best practice" for
mocking and stubbing calls such like these?:

@properties = @user.properties
@property = @user.properties.new
@property = @user.properties.find_by_id params[:id]

Thanks,
Nick
Cdf378de2284d8acf137122e541caa28?d=identicon&s=25 Matt Wynne (mattwynne)
on 2008-11-18 09:44
(Received via mailing list)
On 18 Nov 2008, at 05:41, Nick Hoffman wrote:
> Before writing specs for a one-to-many relationship between two
> models, I did some research and found that some people were creating
> proxy mocks, and others were using Matthew Heidemann's
> #stub_association! (which essentially does that for, but in a nice,
> DRY way):
>  http://www.ruby-forum.com/topic/126993
>
> Are those two methods currently the accepted "best practice" for
> mocking and stubbing calls such like these?:

I don't know about any "best practice". In the realm of TDD vs
ActiveRecord Associations, you're looking at something more like
"least-worst" practice, IMO. The way the AssociationCollections behave
is pretty complex and difficult to simulate with a simple mock or two.

I started out trying to stay 'pure' and not touch the database, but
TBH, these days I've given up the fight and mostly just throw a few
records in a database table - that way you can actually specify the
behaviour you want rather than the gory implementation details.

As you can probably tell by my grumbling, this is one of my least
favourite bits of working on rails.

> @properties = @user.properties
> @property = @user.properties.new
> @property = @user.properties.find_by_id params[:id]

Saying that, it is often still reasonable, I think, to fake an
association proxy collection using an array that's patched with a few
extra methods. We have a helper method in the Songkick spec code
that's called something like FakeCollection, which subclasses array
and has a few helpful methods to make it look enough like an
association collection to make the specs run OK.

HTH,
Matt
Afe1e6b75aace67db4b3ac064256b0f1?d=identicon&s=25 Rahoul Baruah (Guest)
on 2008-11-18 14:21
(Received via mailing list)
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


On 18 Nov 2008, at 05:41, Nick Hoffman wrote:

> @properties = @user.properties
> @property = @user.properties.new
> @property = @user.properties.find_by_id params[:id]


There's also Luke Redpath's Demeter's Revenge plugin
(http://www.lukeredpath.co.uk/2007/10/18/demeters-revenge
  or git cloned to http://github.com/caius/demeters_revenge/tree/
master).

This means you have to change your implementation code as well, but
lets you write:

@properties = @user.properties
@property = @user.build_property # or @user.create_property
@found_properties = @user.find_properties(:conditions => { :some
=> :values })
@user.has_no_properties? # @user.properties.empty?
@user.has_properties? # !@user.properties.empty?
and a few others.

Of course, this also makes it very easy to stub the methods for each
association.

Baz.

Rahoul Baruah
Web design and development: http://www.3hv.co.uk/
Nottingham Forest: http://www.eighteensixtyfive.co.uk/
Serious Rails Hosting: http://www.brightbox.co.uk/
Lifecast: http://www.madeofstone.net/






-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.8 (Darwin)

iEYEARECAAYFAkkitgMACgkQu0BNRvjN8xTmvwCZAZt2nQTv862fHh/8k0rfqW7p
wJIAnAikW7RStJ8OCoV2eWYqs7voz5Py
=tXFK
-----END PGP SIGNATURE-----
C694a032be7518a0d704318895f8fe1d?d=identicon&s=25 Ben Mabey (mabes)
on 2008-11-18 21:50
(Received via mailing list)
Nick Hoffman wrote:
> @properties = @user.properties
> @property = @user.properties.new
> @property = @user.properties.find_by_id params[:id]
>
> Thanks,
> Nick
> _______________________________________________
> rspec-users mailing list
> rspec-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users


I like to place this in my spec_helper.rb:

module Spec
  module Mocks
    module Methods
      def stub_association!(association_name, methods_to_be_stubbed =
{})
        mock_association = Spec::Mocks::Mock.new("#{association_name}
association")
        methods_to_be_stubbed.each do |method, return_value|
          mock_association.stub!(method).and_return(return_value)
        end
        self.stub!(association_name).and_return(mock_association)
        mock_association
      end
    end
  end
end



The you can say stuff like:

@user.stub_association!(:properties, :new => mock_model(Property),
:find_by_id => mock_model(Property))

HTH,
Ben
1b32ae81cae0813e933ca2f8be830c9f?d=identicon&s=25 Chris Flipse (Guest)
on 2008-11-19 18:26
(Received via mailing list)
I've actually taken this old gem and enhanced it a bit

module Spec::Mocks::Methods
  def stub_association!(association_name, methods_to_be_stubbed ={})
    mock_assn = Spec::Mocks::Mock.new(association_name.to_s)
    stub_association_with(association_name, mock_assn,
methods_to_be_stubbed)
  end

  def stub_association_with(association_name, values,
methods_to_be_stubbed
= {})
    methods_to_be_stubbed.each do |meth, return_value|
        values.stub!(meth).and_return(return_value)
    end
    yield(values) if block_given?

    self.stub!(association_name).and_return(values)
  end
end

This lets me specify the "contents" of an association:

foo.stub_association_with(:bar, [@bar1, @bar2, @bar3], :find => @bar1)

and also gives me some more fine grained control about stubbing the
association

foo.stub_association_with(:bar, [@bar1, @bar2, @bar3]) do |assn|
  assn.stub!(:find).with(1).and_return @bar1
  assn.stub!(:find).with(2).and_return @bar2
  assn.stub!(:find).with(5).and_raise RecordNotFound
end
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (nickh)
on 2008-11-20 18:55
(Received via mailing list)
On 2008-11-18, at 07:33, Rahoul Baruah wrote:
>>
>> @properties = @user.properties
>> @property = @user.properties.new
>> @property = @user.properties.find_by_id params[:id]
>
>
> There's also Luke Redpath's Demeter's Revenge plugin 
(http://www.lukeredpath.co.uk/2007/10/18/demeters-revenge
>  or git cloned to http://github.com/caius/demeters_revenge/tree/master)
> .

Thanks for pointing out that article, Baz. Luke's suggestions are an
interesting method of dealing with this. I'm going to give it a try
when I have some time.
-Nick
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (nickh)
on 2008-11-20 18:55
(Received via mailing list)
On 2008-11-19, at 12:26, Chris Flipse wrote:
> methods_to_be_stubbed = {})
>
> foo.stub_association_with(:bar, [@bar1, @bar2, @bar3], :find => @bar1)
>
> and also gives me some more fine grained control about stubbing the
> association
>
> foo.stub_association_with(:bar, [@bar1, @bar2, @bar3]) do |assn|
>   assn.stub!(:find).with(1).and_return @bar1
>   assn.stub!(:find).with(2).and_return @bar2
>   assn.stub!(:find).with(5).and_raise RecordNotFound
> end

Sending in a block like that is a great idea.
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (nickh)
on 2008-11-20 18:55
(Received via mailing list)
On 2008-11-18, at 15:49, Ben Mabey wrote:
>>
> module Mocks
>     end
>
> HTH,
> Ben

That's very similar to Matthew Heidemann's #stub_association! . I
can't say who wrote it first, though  =)  Regardless, that's what I've
been using, and find it to be quite efficient.
-Nick
C694a032be7518a0d704318895f8fe1d?d=identicon&s=25 Ben Mabey (mabes)
on 2008-11-20 19:00
(Received via mailing list)
Nick Hoffman wrote:
>>> mocking and stubbing calls such like these?:
>> module Spec
>>     end
>>
> http://rubyforge.org/mailman/listinfo/rspec-users
Probably Matt.. I can't remember who posted it first... But I got it
from the list a while ago and it has been very useful in a lot of my
apps.
-Ben
This topic is locked and can not be replied to.