Not seeing the failure

All,

I’m missing something simple, I think. I am writing a spec to say that
my
CouponController should create a new coupon from the form parameters,
then
set the current user. Here’s the spec:

describe CouponController, “When posting to save_coupon” do

before(:each) do
@expectedName = “pepper’s”
@expectedAmount = 5

coupon = mock_model Coupon
current_user = mock_model User
controller.stub!(:current_user).and_return(current_user)
Coupon.should_receive

(:new).with({“name”=>@expectedName,“amount”=>@expectedAmount}).and_return(coupon)
coupon.should_receive(:user).with(current_user)
coupon.should_receive(:save)
end

it “should tell the Coupon model to create a new coupon with the given
parameters and save” do
post
‘save_coupon’,{:coupon=>{:name=>@expectedName,:amount=>@expectedAmount}}
end

Here’s the controller method:

def save_coupon
coupon = Coupon.new(params[:coupon])
coupon.user = current_user
coupon.save
redirect_to_index “Coupon Added!”
end

And, I get the following failure:

Mock ‘Coupon_1008’ received unexpected message :user= with
(#<User:0x221a3e8
@name=“User_1009”>)

I’m sure that I’m missing something very simple, but I’ve been staring
at it
for too long.

(also, if anyone has commented on my style, please feel free to mention
it,
as I’m still adjusting my mind to rspec)

Oh, versions, I almost forgot:
rails 2.0.2
rspec(_on_rails) plugins just updated from current a couple days ago,
not
totally sure how to get the versions of the plugins

Thanks.
-Corey

On Jan 23, 2008 8:41 AM, Corey H. [email protected] wrote:

@expectedAmount = 5

it "should tell the Coupon model to create a new coupon with the given
coupon.save
redirect_to_index “Coupon Added!”
end

And, I get the following failure:

Mock ‘Coupon_1008’ received unexpected message :user= with (#<User:0x221a3e8
@name=“User_1009”>)

That’s from this line in save_coupon:

coupon.user = current_user

Just need to stub that:

coupon.stub!(:user=)

Or you could expect it:

coupon.should_receive(:user=).with(current_user)

Cheers,
David

Oh, and one follow-up, the app works how I expect it to work if I run
through the browser.

Of course. Thanks, David! I still am getting used to user=, rather than
just
user. Thanks again.

-Corey

On Jan 23, 2008 8:49 AM, Corey H. [email protected] wrote:

Of course. Thanks, David! I still am getting used to user=, rather than just
user. Thanks again.

No problem. I certainly got caught by that early on.

I have some more general comments. See below:

before(:each) do
coupon.should_receive(:save)
def save_coupon
coupon = Coupon.new(params[:coupon])
coupon.user = current_user
coupon.save
redirect_to_index “Coupon Added!”
end

This one example is doing too much IMO. You even say “I am writing a
spec to say that my CouponController should create a new coupon from
the form parameters, then set the current user.” That’s two things.

Generally I try to keep stubs in before(:each), expectations in the
examples, and one example for each concept that I’m describing.

I took the liberty of pastie-ing what I’d probably do. I haven’t run
it, so it might not work as/is, but you’ll get the idea.

http://pastie.caboo.se/142403

Cheers,
David

PS - I’m really glad to see you getting involved with this list.

Thanks, David!

Here’s what I morphed the specs into:

http://pastie.caboo.se/142411

And, I ended up with a blog entry that I’ll write tonight.

Basically, here’s the situation I’ve been running into which is causing
my
specs to grow.

If I don’t set up something to tell the coupon that it is going to have
:user= called on it, then the other tests fails. I didn’t want to stub
it,
as I want this to be part of my expectations. Luckily, it only took a
minute
to come up with a possible solution, again relying on the fact that
things
tend to work how I think they should.

The solution I thought of was to set the stub! call in the
before(:each),
then set an actual expectation in the spec. In my mind, the expectation
should override the stub (making sure it gets called), but the stub will
allow the other tests to pass. Fantastic! It works. I’m going to write
up a
blog entry on this pattern, and I’ll send a link when I post it.

Thanks for your help.
-Corey

On Jan 23, 2008 9:35 AM, David C. [email protected] wrote:

specs to grow.
Actually - that’s what mine did to. These two are the same:
:slight_smile:
blog entry on this pattern, and I’ll send a link when I post it.
FYI -
http://blog.davidchelimsky.net/articles/2006/11/09/tutorial-rspec-stubs-and-mocks

Strange. I’ll try it again, but it failed, which was why I added the
stub.
Let me see if I mis-typed something.

On Jan 23, 2008 9:24 AM, Corey H. [email protected] wrote:

If I don’t set up something to tell the coupon that it is going to have
:user= called on it, then the other tests fails. I didn’t want to stub it,
as I want this to be part of my expectations. Luckily, it only took a minute
to come up with a possible solution, again relying on the fact that things
tend to work how I think they should.

The solution I thought of was to set the stub! call in the before(:each),
then set an actual expectation in the spec.

Actually - that’s what mine did to. These two are the same:

coupon = mock_model(Coupon,
:user= => nil,
:save => true
)

coupon = mock_model Coupon
coupon.stub!(:user=)
coupon.stub!(:save).and_return(true)

:slight_smile:

Looking forward to your blog.

Cheers,
David

That’s what I found. :slight_smile: I’ll reference that with my blog post. (I’ll
write
it mostly for the experience, as it will basically outline just what
your
post says)

I get the unexpected message :user= failure.

When you set up the mock with the initial parameters, wouldn’t those be
stubbing: @coupon.stub!(:user).and_return(@current_user)

While, my spec says that I want :user= to be called?

-corey

On 23.1.2008, at 18.02, Corey H. wrote:

I get the unexpected message :user= failure.

When you set up the mock with the initial parameters, wouldn’t those
be stubbing: @coupon.stub!(:user).and_return(@current_user)

You can do it like this:

@coupon = mock_model(Coupon, :user= => nil, :user =>
@current_user, :save => true)

That stubs both user= and user. I often create reusable “mock
factories” in spec_helper.rb that take care of the normal methods for
AR objects:

def mock_coupon(opts = {})
mock_model(Coupon, {:user= => nil, :user => mock_user, :save =>
true}.merge(opts))
end

Often I also stub the errors method there because it’s called pretty
often in controllers and views.

//jarkko


Jarkko L.
http://jlaine.net
http://dotherightthing.com
http://www.railsecommerce.com
http://odesign.fi

Nope, that still didn’t work.
Still getting
@coupon.stub!(:user=).with(@current_user)

Here’s my spec and code using your recommended way:

spec - http://pastie.caboo.se/142451
code - http://pastie.caboo.se/142452

If I add back
@coupon.stub!(:user=).with(@current_user)

they pass.

It seems as though the understanding is that setting up your mock with
the
options (:user => @current_user) should set stubs for both get and set,
but
it doesn’t. I think it just sets up the stub for the get.

-Corey

Ooops, I mean still getting
Mock ‘Coupon_1011’ received unexpected message :user= with
(#<User:0x22087ba
@name=“User_1010”>)

On 23.1.2008, at 18.57, Corey H. wrote:

@coupon.stub!(:user=).with(@current_user)

they pass.

It seems as though the understanding is that setting up your mock
with the options (:user => @current_user) should set stubs for both
get and set, but it doesn’t. I think it just sets up the stub for
the get.

I didn’t say it would (and it doesn’t and shouldn’t):

You can do it like this:

@coupon = mock_model(Coupon, :user= => nil, :user =>
@current_user, :save => true)

Note both :user= and :user.

In your pastie you only have :user

Cheers,
//jarkko


Jarkko L.
http://jlaine.net
http://dotherightthing.com
http://www.railsecommerce.com
http://odesign.fi

No, the mock is set up to return nil when user= is called. Then he
writes an example to actually expect the call to user=.

Pat

Ah, I completely missed the :user= => in your snippet. Sorry about that.
It
worked, though, just the way you described it. :slight_smile:

Here’s my spec now, wonderful:
http://pastie.caboo.se/142585

Thanks, David and Jarkko!

-Corey

Thanks, Pat.

-Corey

I just wanted to put one last note here (I will be blogging about this).

WOW! I absolutely love that initialization syntax. I just went through
some
old specs and replaced my stubs with the initialization hash on the mock
creation. Really sweet.

-Corey