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.
059ed46172a087063ce26250e44c8627?d=identicon&s=25 Fernando Perez (fernando)
on 2008-11-05 20: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?
059ed46172a087063ce26250e44c8627?d=identicon&s=25 Fernando Perez (fernando)
on 2008-11-05 20: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.
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2008-11-05 21:01
(Received via mailing list)
On Wed, Nov 5, 2008 at 1:55 PM, Fernando Perez <lists@ruby-forum.com>
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 :)
059ed46172a087063ce26250e44c8627?d=identicon&s=25 Fernando Perez (fernando)
on 2008-11-05 21: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)?
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (Guest)
on 2008-11-05 21:15
(Received via mailing list)
On 2008-11-05, at 14:55, Fernando Perez 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
5d38ab152e1e3e219512a9859fcd93af?d=identicon&s=25 David Chelimsky (Guest)
on 2008-11-05 21:16
(Received via mailing list)
On Wed, Nov 5, 2008 at 2:08 PM, Fernando Perez <lists@ruby-forum.com>
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.
059ed46172a087063ce26250e44c8627?d=identicon&s=25 Fernando Perez (fernando)
on 2008-11-05 21: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.
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (Guest)
on 2008-11-05 21:34
(Received via mailing list)
On 2008-11-05, at 15:23, Fernando Perez 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
49de4cd2f26705785cbef2b15a9df7aa?d=identicon&s=25 Nick Hoffman (Guest)
on 2008-11-05 21:46
(Received via mailing list)
On 2008-11-05, at 15:23, Fernando Perez 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
059ed46172a087063ce26250e44c8627?d=identicon&s=25 Fernando Perez (fernando)
on 2008-11-05 21: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.