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