Forum: RSpec RSpec whines when I set the value of an object

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Fernando P. (Guest)
on 2008-11-05 21:55
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?
Fernando P. (Guest)
on 2008-11-05 21:59
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.
David C. (Guest)
on 2008-11-05 22:01
(Received via mailing list)
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 :)
Fernando P. (Guest)
on 2008-11-05 22:08
> @cart.should_receive(:amount=).with(50)
>
> amount and amount= are two different methods :)

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)?
Nick H. (Guest)
on 2008-11-05 22:15
(Received via mailing list)
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
David C. (Guest)
on 2008-11-05 22:16
(Received via mailing list)
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.
Fernando P. (Guest)
on 2008-11-05 22:23
> 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.
Nick H. (Guest)
on 2008-11-05 22:34
(Received via mailing list)
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
Nick H. (Guest)
on 2008-11-05 22:46
(Received via mailing list)
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
Fernando P. (Guest)
on 2008-11-05 22:53
> 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.
This topic is locked and can not be replied to.