James Hillyerd
2007-Apr-19 00:07 UTC
[rspec-users] Best practice thoughts: Model helpers, mocks
Hello! I''m pretty new to unit testing, and have only been using RSpec for a few weeks. I found that for my controller specs, my setup methods were getting very long building mocks/stubs for all the model objects I needed to work with. I''ve started creating helpers like the following for each of my models. The "min_" methods are short for "minimum", and are there to give me the minimum set of data that will pass validation for that model. The "obj.send" code is to reduce the length of the data output when RSpec compares expectations to results... A lot of my mocks were several screenfulls long which sucks. I''ve also started adding code that takes the attribs I define for the mock_ method and stubs out :setter= methods too. Am I going down the right path here? (If so, I have a text mate snippet that creates this stuff pretty quickly I can share) module RenewalSpecHelper def min_renewal_attribs { :member_id => 1, :order_id => 1, :date_renewed => Date.new(2007, 1, 1), :membership_type_id => 1, :shirt_size => ''M'', :amount_due => ''5000'' } end def min_renewal Renewal.new :attributes => min_renewal_attribs end def mock_renewal(stubs = {}) obj = mock_model(Renewal, { :member_id => 1, :date_renewed => Date.new(2007, 1, 1), :membership_type_id => 1, :shirt_size => ''M'', :billing_method => ''BM'', :amount_due => ''5000'' }.merge!(stubs)) obj.send(:__mock_handler).instance_eval "def inspect; @name; end" obj end end Below is an example of what I''d like to avoid, one of my controller test setups. It seems like each context, I end up pasting the previous contexts setup, and then adding a few more lines, until it ends up... setup do mem_types = Array.new @membership_type1 = mock_model(MembershipType) @membership_type1.stub!(:id).and_return(1) @membership_type1.stub!(:name).and_return(''Name'') @membership_type1.stub!(:price_cents).and_return(5000) mem_types << @membership_type1 @membership_type2 = mock_model(MembershipType) @membership_type2.stub!(:id).and_return(2) @membership_type2.stub!(:name).and_return(''Name'') @membership_type2.stub!(:price_cents).and_return(10000) mem_types << @membership_type2 @membership_type3 = mock_model(MembershipType) @membership_type3.stub!(:id).and_return(3) @membership_type3.stub!(:name).and_return(''Name'') @membership_type3.stub!(:price_cents).and_return(15000) mem_types << @membership_type3 MembershipType.stub!(:find_available).and_return(mem_types) addl_members = Array.new [ 1, 2, 3, 3 ].each do |i| am = mock_model(AdditionalMember) am.stub!(:blank?).and_return(false) am.stub!(:membership_type_id).and_return(i) am.stub!(:email).and_return(''foo at bar.com'') am.stub!(:first_name).and_return(''John'') am.stub!(:last_name).and_return(''Doh'') am.stub!(:gender).and_return(''M'') am.stub!(:shirt_size).and_return(''L'') # am.stub!(:).and_return() addl_members << am end @signup_checkout = mock_model(SignupCheckout) @signup_checkout.stub!(:valid?).and_return(true) @signup_checkout.stub!(:shirt_size).and_return(''L'') @signup_checkout.stub!(:billing_method).and_return(''PP'') @signup_checkout.stub!(:additional_members).and_return(addl_members) session[:signup_checkout] = @signup_checkout @member = mock_model(Member) @member.stub!(:valid?).and_return(true) @member.stub!(:membership_type_id).and_return(1) @member.stub!(:membership_type).and_return(@membership_type1) @member.stub!(:full_name).and_return(''Some Body'') @member.stub!(:addr_line1).and_return(''123 Main'') @member.stub!(:addr_line2).and_return(nil) @member.stub!(:city).and_return(''City'') @member.stub!(:province).and_return(''WA'') @member.stub!(:postal_code).and_return(''98008'') @member.stub!(:visible_to_public).and_return(true) @member.stub!(:email_newsletter).and_return(true) # @member.stub!(:).and_return() @member.stub!(:lifetime_member=) @member.stub!(:membership_expires_on=) @member.stub!(:save!) session[:member] = @member end (I have since learned that mock_models do stub out a unique ID value, so I don''t set those by hand on my newer tests) Thanks for any opinions or insight. :) -james -- James A. Hillyerd <james at hillyerd.com>
Courtenay
2007-Apr-19 05:59 UTC
[rspec-users] Best practice thoughts: Model helpers, mocks
> setup do > mem_types = Array.new > @membership_type1 = mock_model(MembershipType) > @membership_type1.stub!(:id).and_return(1) > @membership_type1.stub!(:name).and_return(''Name'') > @membership_type1.stub!(:price_cents).and_return(5000)this is a little shorter: @membership_type1 = mock_model(MembershipType, :id => 1, :name => ''Name'', :price_cents => 5000 also, some questions; Are you really using all those fields in all your specs? if so, looks like some of your code can be pushed to the model. There''s rarely a need to define all those stubs, unless of course you have some killer logic. Stub out the fields you are doing logic with, and forget the rest. Finally, check out http://blog.caboo.se/articles/2007/4/16/dynamic-rspec-setup-methods as a way to modularize your setup methods. I am still learning this stuff myself, but a controller setup in my experience usually runs to just a few lines.
Matthijs Langenberg
2007-Apr-19 08:04 UTC
[rspec-users] Best practice thoughts: Model helpers, mocks
I guess you are using integrated views, aren''t you? Focus on the behaviour of your controller method, I don''t think it would ever need all those stubbed fields, but please prove me wrong. On 4/19/07, Courtenay <court3nay at gmail.com> wrote:> > setup do > > mem_types = Array.new > > @membership_type1 = mock_model(MembershipType) > > @membership_type1.stub!(:id).and_return(1) > > @membership_type1.stub!(:name).and_return(''Name'') > > @membership_type1.stub!(:price_cents).and_return(5000) > > this is a little shorter: > > @membership_type1 = mock_model(MembershipType, :id => 1, :name => > ''Name'', :price_cents => 5000 > > also, some questions; > > Are you really using all those fields in all your specs? if so, looks > like some of your code can be pushed to the model. There''s rarely a > need to define all those stubs, unless of course you have some killer > logic. Stub out the fields you are doing logic with, and forget the > rest. > > Finally, check out > > http://blog.caboo.se/articles/2007/4/16/dynamic-rspec-setup-methods > > as a way to modularize your setup methods. > > I am still learning this stuff myself, but a controller setup in my > experience usually runs to just a few lines. > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >
James Hillyerd
2007-Apr-19 16:24 UTC
[rspec-users] Best practice thoughts: Model helpers, mocks
I''m not using integrated views, actually not doing any view testing at all right now since 0.8.2 doesn''t work with HAML. Not sure I would even if I could though. Part of the reason that setup method has so many objects is that it''s part of a wizard, so I am re-validating everything I have stored in the session. The wizard is also converting one object (signup_checkout) into a bunch of other objects (members * 4, renewals * 4). I probably should look at pushing some of my code into my model... But it seems like code dealing with a form object that is never saved to the database (signup_checkout is an "ActiveForm" http://www.realityforge.org/articles/2005/12/02/validations-for-non-activerecord-model-objects) doesn''t belong in the model, so I''m a little torn. -james On 4/19/07, Matthijs Langenberg <mlangenberg at gmail.com> wrote:> I guess you are using integrated views, aren''t you? Focus on the > behaviour of your controller method, I don''t think it would ever need > all those stubbed fields, but please prove me wrong. >[..snip] -- James A. Hillyerd <james at hillyerd.com> Chief Technical Officer - ActiveRain Corp
Why doesn''t 0.8.2 work with haml? I''ve never had any issues with haml and I''ve been using it and rSpec for a while now... On Apr 19, 2007, at 9:24 AM, James Hillyerd wrote:> I''m not using integrated views, actually not doing any view testing at > all right now since 0.8.2 doesn''t work with HAML. Not sure I would > even if I could though. > > Part of the reason that setup method has so many objects is that it''s > part of a wizard, so I am re-validating everything I have stored in > the session. The wizard is also converting one object > (signup_checkout) into a bunch of other objects (members * 4, renewals > * 4). > > I probably should look at pushing some of my code into my model... But > it seems like code dealing with a form object that is never saved to > the database (signup_checkout is an "ActiveForm" > http://www.realityforge.org/articles/2005/12/02/validations-for-non- > activerecord-model-objects) > doesn''t belong in the model, so I''m a little torn. > > -james > > On 4/19/07, Matthijs Langenberg <mlangenberg at gmail.com> wrote: >> I guess you are using integrated views, aren''t you? Focus on the >> behaviour of your controller method, I don''t think it would ever need >> all those stubbed fields, but please prove me wrong. >> > [..snip] > > -- > James A. Hillyerd <james at hillyerd.com> > Chief Technical Officer - ActiveRain Corp > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users
James Hillyerd
2007-Apr-19 20:54 UTC
[rspec-users] Best practice thoughts: Model helpers, mocks
"rake spec" works fine with HAML, but using the DRb server and rake spec:autotest does not at least for me. I think I''m on Rails 1.2.3 w/ HAML 1.5. -james On 4/19/07, s.ross <cwdinfo at gmail.com> wrote:> Why doesn''t 0.8.2 work with haml? I''ve never had any issues with haml > and I''ve been using it and rSpec for a while now... > > On Apr 19, 2007, at 9:24 AM, James Hillyerd wrote: > > > I''m not using integrated views, actually not doing any view testing at > > all right now since 0.8.2 doesn''t work with HAML. Not sure I would > > even if I could though. > > > > Part of the reason that setup method has so many objects is that it''s > > part of a wizard, so I am re-validating everything I have stored in > > the session. The wizard is also converting one object > > (signup_checkout) into a bunch of other objects (members * 4, renewals > > * 4). > > > > I probably should look at pushing some of my code into my model... But > > it seems like code dealing with a form object that is never saved to > > the database (signup_checkout is an "ActiveForm" > > http://www.realityforge.org/articles/2005/12/02/validations-for-non- > > activerecord-model-objects) > > doesn''t belong in the model, so I''m a little torn. > > > > -james > > > > On 4/19/07, Matthijs Langenberg <mlangenberg at gmail.com> wrote: > >> I guess you are using integrated views, aren''t you? Focus on the > >> behaviour of your controller method, I don''t think it would ever need > >> all those stubbed fields, but please prove me wrong. > >> > > [..snip] > > > > -- > > James A. Hillyerd <james at hillyerd.com> > > Chief Technical Officer - ActiveRain Corp > > _______________________________________________ > > rspec-users mailing list > > rspec-users at rubyforge.org > > http://rubyforge.org/mailman/listinfo/rspec-users > > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- James A. Hillyerd <james at hillyerd.com> Chief Technical Officer - ActiveRain Corp