RSpec whines when I set the value of an object


#1

In have the following code:

def index
@items = Item.find_for_payment(session[:order_id], @site.id)
@cart.amount = @items.sum { |item| item.price.to_i *
item.quantity.to_i }
end

@cart is set by a before_filter called find_cart which I stub.
find_for_payment is stubbed too.

In my spec I simply test for the following:

it “should display cart” do
@items = []
1.upto(2) do |i|
@items << mock_model(Item, :id => i, :price => 10 * i, :quantity
=> i, :order_id => 1)
end

Item.stub!(:find_for_payment).and_return(@items)

@cart = mock_model(Order, :id => 1, :amount => 0, :tax => 0)
Order.stub!(:find_cart).and_return(@cart)

Item.should_receive(:find_for_payment).with(1, 1).and_return(@items)
get :index

end

And I get the following error message:

Mock ‘Order_1’ received unexpected message :amount= with (50)

Well I know that @cart.amount will be set to 50, but why is RSpec
complaining about that?

If I put @cart.should_receive(:amount).with(50), rspec still throws an
error message. What to do?


#2

To make RSpec happy, I tried to test for the following:

@cart.amount.should eql(50)

And now I get:

FAILED
expected 50, got 0 (using .eql?)

So is @cart.amount being set to 50 or not in the spec? When I test
manually it works fine.


#3

On Wed, Nov 5, 2008 at 1:55 PM, Fernando P. removed_email_address@domain.invalid
wrote:

Mock ‘Order_1’ received unexpected message :amount= with (50)

Well I know that @cart.amount will be set to 50, but why is RSpec
complaining about that?

If I put @cart.should_receive(:amount).with(50), rspec still throws an
error message. What to do?

@cart.should_receive(:amount=).with(50)

amount and amount= are two different methods :slight_smile:


#4

@cart.should_receive(:amount=).with(50)

amount and amount= are two different methods :slight_smile:

Thanks Dave!

So now if I test for: @cart.amount.should eql(50)

Why do I get this error: expected 50, got 0 (using .eql?)

I have to add that the @cart is not saved in DB after its amount
attribute is set, it is only displayed in the view for the user to see
what would be the total amount.

Does the @cart object need to be saved to be able to test it with should
eql(50)?


#5

On Wed, Nov 5, 2008 at 2:08 PM, Fernando P. removed_email_address@domain.invalid
wrote:

I have to add that the @cart is not saved in DB after its amount
attribute is set, it is only displayed in the view for the user to see
what would be the total amount.

Does the @cart object need to be saved to be able to test it with should
eql(50)?

Fernando - I’m happy to help, as are we all, but you make it difficult
to understand the context if we have to look at other email in the
thread to see the code.

Please make sure, when you ask a question, that you quote enough of
the thread so that the readers can understand what you’re asking.


#6

Please make sure, when you ask a question, that you quote enough of
the thread so that the readers can understand what you’re asking.
Hmmm, I don’t have this problem as I am using ruby-forum.com to browse
threads, it is x100 times more readable with basic color highlighting.
I’ll do my best to include quotes for people who use regular mail
clients.

So here is my controller code:

def index
@items = Item.find_for_payment(session[:order_id], @site.id)
@cart.amount = @items.sum { |item| item.price.to_i *
item.quantity.to_i }
end

And my spec:

it “should display cart” do
@items = []
1.upto(2) do |i|
@items << mock_model(Item, :id => i, :price => 10 * i, :quantity
=> i, :order_id => 1)
end

Item.stub!(:find_for_payment).and_return(@items)

@cart = mock_model(Order, :id => 1, :amount => 0, :tax => 0)
Order.stub!(:find_cart).and_return(@cart)

Item.should_receive(:find_for_payment).with(1, 1).and_return(@items)
@cart.should_receive(:amount=).with(50)
@cart.amount.should eql(50)

get :index

end

This throws the error: expected 50, got 0 (using .eql?)

As I said, I don’t actually save the @cart object in DB, I just
temporarily set its amount attribute so that it will print the value in
the corresponding view. I am now wondering why RSpec doesn’t see it is
set to 50.


#7

On 2008-11-05, at 15:23, Fernando P. wrote:

This throws the error: expected 50, got 0 (using .eql?)

This is because you told @cart to return 0 when #amount is called on it:
@cart = mock_model(Order, :id => 1, :amount => 0, :tax => 0)

I think maybe you’re confusing what the arguments to #mock_model do.
They don’t set attributes’ values in the mock object. The first
argument specifies which class is being mocked. In this case, Order.
The other arguments tell the mock what value to return for a given
method. So this:
@cart = mock_model Order, :amount => 0
tells the mock object in @cart to respond to #amount , and return the
value 0 (zero).

Does that make things a bit clearer? Cheers,
Nick


#8

On 2008-11-05, at 15:23, Fernando P. wrote:

item.quantity.to_i }
end

set to 50.
This expectation:
@cart.amount.should eql(50)
is just testing Order#amount= . In my opinion, that’s not necessary,
because Order#amount= is provided by Rails, which you can be confident
has been tested fully.

This expectation:
@cart.should_receive(:amount=).with(50)
is what you really want, because it’s ensuring that Order#amount= is
being called with the correct value.

So, just remove the “should eql(50)” expectation, and you’re all set!
-Nick


#9

On 2008-11-05, at 14:55, Fernando P. wrote:

error message. What to do?
Hi Fernando. The error’s occuring because #amount= hasn’t been defined
within the “Order_1” mock object. Try this:
@cart.should_receive(:amount=).with 50

Cheers,
Nick


#10

This expectation:
@cart.should_receive(:amount=).with(50)
is what you really want, because it’s ensuring that Order#amount= is
being called with the correct value.

So, just remove the “should eql(50)” expectation, and you’re all set!
-Nick

You are perfectly right Nick, I had just noticed that behavior which I
couldn’t understand so that’s why I wanted to know what was behind it.
At the same time it taught how exactly mock_model works which I first
got completely wrong.

Thanks for your assistance.