Stub / Mock - A little guidance?

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:

‘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 H.

Op 19-mrt-08, om 16:33 heeft Dave het volgende geschreven:

attributes (Ignore the clunky design… that’s just how it has to be).
self.bill_to_country_code = billing_info.country_code
end

If this is activerecord you could use
self.update_attributes(billing_info)

                               :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!

end

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