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
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
Rahoul Baruah
2008-Nov-18 12:33 UTC
[rspec-users] Mocking and stubbing model relationships
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 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?: > > @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-----
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?: > > @properties = @user.properties > @property = @user.properties.new > @property = @user.properties.find_by_id params[:id] > > Thanks, > Nick > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersI 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
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 On Tue, Nov 18, 2008 at 3:49 PM, Ben Mabey <ben at benmabey.com> wrote:> 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 > > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- // anything worth taking seriously is worth making fun of // http://blog.devcaffeine.com/ -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/rspec-users/attachments/20081119/15ac8678/attachment.html>
On 2008-11-18, at 07:33, Rahoul Baruah wrote:> 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?: >> >> @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
On 2008-11-18, at 15:49, Ben Mabey wrote:> 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?: >> >> @properties = @user.properties >> @property = @user.properties.new >> @property = @user.properties.find_by_id params[:id] >> >> Thanks, >> Nick > > 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, > BenThat''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
On 2008-11-19, at 12:26, Chris Flipse wrote:> 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 > endSending in a block like that is a great idea.
Nick Hoffman wrote:> On 2008-11-18, at 15:49, Ben Mabey wrote: >> 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?: >>> >>> @properties = @user.properties >>> @property = @user.properties.new >>> @property = @user.properties.find_by_id params[:id] >>> >>> Thanks, >>> Nick >> >> 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 > > 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 > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-usersProbably 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