Hi there,
I''m still trying to wrap my head around when to use a stub and a mock.
If I understand this right, I should be using a ''mock'' when
imitating
an object, but not its behavior. I should be using a stub when I want
to imitate the behavior of an object. Does that sound about right?
With that said, I''m struggling a little trying to spec out this
instance method that is available to a User object. The method takes
an OrderPaymentInfo object and copies its attributes onto the User''s
attributes (Ignore the clunky design... that''s just how it has to be).
The User''s instance method looks like so:
class User
def update_billing_info(billing_info)
unless self.has_billing_address?
self.bill_to_address1 = billing_info.address1
self.bill_to_address2 = billing_info.address2
self.bill_to_address3 = billing_info.address3
self.bill_to_city = billing_info.city
self.bill_to_country_code = billing_info.country_code
self.bill_to_state_province_code = billing_info.state_province_code
self.bill_to_postal_code = billing_info.postal_code
self.bill_to_phone_number = billing_info.phone_number
self.bill_to_extension = billing_info.extension
self.bill_to_fax = billing_info.fax_number
self.save ? true : false
else
true
end
end
Here''s the spec:
describe User, "when checking billing information" do
before(:each) do
@user = User.new(:id => 1)
@order_payment_info = mock_model(OrderPaymentInfo,
:id => 1,
:user_id => @user.id,
:address1 => "555 Rd.",
:address2 => "Ste 2",
:address3 => "line 3",
:city => "Chicago",
:country_code => "USA",
:state_province_code => "IL",
:psotal_code => "12345",
:phone_number => "5551234321",
:extension => "123",
:fax_number => "5551234321")
@user.stub!(:update_billing_info).with(@order_payment_info)
end
it "should update billing information when User''s billing info
is nil" do
@user.should_receive(:update_billing_info).with(@order_payment_info)
@user.update_billing_info(@order_payment_info)
@user.stub!(:has_billing_address?).and_return(false)
@user.bill_to_address1.should eql("555 Rd.")
@user.bill_to_address2.should eql("Ste 2")
end
end
When I run this spec I get an "error" of:
1)
''User when checking billing information should update billing
information when User''s billing info is nil'' FAILED
expected "555 Rd.", got nil (using .eql?)
I''m just wondering what the best way to approach this would be.
Thank you,
Dave Hoefler
Op 19-mrt-08, om 16:33 heeft Dave het volgende geschreven:> Hi there, > > I''m still trying to wrap my head around when to use a stub and a mock. > If I understand this right, I should be using a ''mock'' when imitating > an object, but not its behavior. I should be using a stub when I want > to imitate the behavior of an object. Does that sound about right? > > With that said, I''m struggling a little trying to spec out this > instance method that is available to a User object. The method takes > an OrderPaymentInfo object and copies its attributes onto the User''s > attributes (Ignore the clunky design... that''s just how it has to be). > > The User''s instance method looks like so: > > class User > def update_billing_info(billing_info) > unless self.has_billing_address? > self.bill_to_address1 = billing_info.address1 > self.bill_to_address2 = billing_info.address2 > self.bill_to_address3 = billing_info.address3 > self.bill_to_city = billing_info.city > self.bill_to_country_code = billing_info.country_code > self.bill_to_state_province_code = > billing_info.state_province_code > self.bill_to_postal_code = billing_info.postal_code > self.bill_to_phone_number = billing_info.phone_number > self.bill_to_extension = billing_info.extension > self.bill_to_fax = billing_info.fax_number > self.save ? true : false > else > true > end > end >If this is activerecord you could use self.update_attributes(billing_info)> > Here''s the spec: > > describe User, "when checking billing information" do > before(:each) do > @user = User.new(:id => 1) > @order_payment_info = mock_model(OrderPaymentInfo, > :id => 1, > :user_id => @user.id, > :address1 => "555 Rd.", > :address2 => "Ste 2", > :address3 => "line 3", > :city => "Chicago", > :country_code => "USA", > :state_province_code => "IL", > :psotal_code => "12345", > :phone_number => "5551234321", > :extension => "123", > :fax_number => "5551234321") > @user.stub!(:update_billing_info).with(@order_payment_info) > endYou just stubbed update_billing_info (called with @order_payment_info) on @user, which means the real method will not be used anymore. Your tests will use the stub instead of the real method. Don''t stub the very thing you want to test!> > > it "should update billing information when User''s billing info is > nil" do > > @user.should_receive(:update_billing_info).with(@order_payment_info) > @user.update_billing_info(@order_payment_info) > @user.stub!(:has_billing_address?).and_return(false) > @user.bill_to_address1.should eql("555 Rd.") > @user.bill_to_address2.should eql("Ste 2") > end > > > end > > When I run this spec I get an "error" of: > > 1) > ''User when checking billing information should update billing > information when User''s billing info is nil'' FAILED > expected "555 Rd.", got nil (using .eql?) >Try to test one thing at a time, you shouldn''t test everything about that method in one test. To me your test description does not tell a thing: ''User when checking billing information should update billing information when User''s billing info is nil'' This says much more: ''User instance object update_billing_info should update the billing address part 1'' You can build this up like this: describe User, "instance object" do before(:each) do @user = User.create( ... ) end describe "update_billing_info" do it "should update the billing address part 1" do @user.update_billing_info( @order_payment_info ) @user.bill_to_address1.should == "555 Rd." end it "should ..." ... end end Try to make small tests (make a new test if the first works) and try to build the model with the tests instead of the other way around. gr Ivo
Ivo, Thank you for that explanation! That was exactly what I needed. I feel like I understand things more clearly after that. I just need to remember to keep my tests small. Thanks! Dave On Wed, Mar 19, 2008 at 11:01 AM, Ivo Dancet <ivo.dancet at gmail.com> wrote:> Op 19-mrt-08, om 16:33 heeft Dave het volgende geschreven: > > > > > Hi there, > > > > I''m still trying to wrap my head around when to use a stub and a mock. > > If I understand this right, I should be using a ''mock'' when imitating > > an object, but not its behavior. I should be using a stub when I want > > to imitate the behavior of an object. Does that sound about right? > > > > With that said, I''m struggling a little trying to spec out this > > instance method that is available to a User object. The method takes > > an OrderPaymentInfo object and copies its attributes onto the User''s > > attributes (Ignore the clunky design... that''s just how it has to be). > > > > The User''s instance method looks like so: > > > > class User > > def update_billing_info(billing_info) > > unless self.has_billing_address? > > self.bill_to_address1 = billing_info.address1 > > self.bill_to_address2 = billing_info.address2 > > self.bill_to_address3 = billing_info.address3 > > self.bill_to_city = billing_info.city > > self.bill_to_country_code = billing_info.country_code > > self.bill_to_state_province_code > > billing_info.state_province_code > > self.bill_to_postal_code = billing_info.postal_code > > self.bill_to_phone_number = billing_info.phone_number > > self.bill_to_extension = billing_info.extension > > self.bill_to_fax = billing_info.fax_number > > self.save ? true : false > > else > > true > > end > > end > > > > If this is activerecord you could use > self.update_attributes(billing_info) > > > > > > Here''s the spec: > > > > describe User, "when checking billing information" do > > before(:each) do > > @user = User.new(:id => 1) > > @order_payment_info = mock_model(OrderPaymentInfo, > > :id => 1, > > :user_id => @user.id, > > :address1 => "555 Rd.", > > :address2 => "Ste 2", > > :address3 => "line 3", > > :city => "Chicago", > > :country_code => "USA", > > :state_province_code => "IL", > > :psotal_code => "12345", > > :phone_number => "5551234321", > > :extension => "123", > > :fax_number => "5551234321") > > @user.stub!(:update_billing_info).with(@order_payment_info) > > end > > You just stubbed update_billing_info (called with @order_payment_info) > on @user, which means the real method will not be used anymore. Your > tests will use the stub instead of the real method. Don''t stub the > very thing you want to test! > > > > > > > > it "should update billing information when User''s billing info is > > nil" do > > > > @user.should_receive(:update_billing_info).with(@order_payment_info) > > @user.update_billing_info(@order_payment_info) > > @user.stub!(:has_billing_address?).and_return(false) > > @user.bill_to_address1.should eql("555 Rd.") > > @user.bill_to_address2.should eql("Ste 2") > > end > > > > > > end > > > > When I run this spec I get an "error" of: > > > > 1) > > ''User when checking billing information should update billing > > information when User''s billing info is nil'' FAILED > > expected "555 Rd.", got nil (using .eql?) > > > > Try to test one thing at a time, you shouldn''t test everything about > that method in one test. To me your test description does not tell a > thing: > > > ''User when checking billing information should update billing > information when User''s billing info is nil'' > > This says much more: > > ''User instance object update_billing_info should update the billing > address part 1'' > > You can build this up like this: > > describe User, "instance object" do > before(:each) do > @user = User.create( ... ) > end > describe "update_billing_info" do > it "should update the billing address part 1" do > > @user.update_billing_info( @order_payment_info ) > @user.bill_to_address1.should == "555 Rd." > end > it "should ..." ... > end > end > > > Try to make small tests (make a new test if the first works) and try > to build the model with the tests instead of the other way around. > > gr > Ivo > _______________________________________________ > rspec-users mailing list > rspec-users at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >