Parameter evaluation for macros

0 views
Skip to first unread message

Curran

unread,
Sep 29, 2009, 2:52:49 PM9/29/09
to shoulda
Hi,

I'd like to write the following test inside a functional test
(subclass of ActionController::TestCase):

should_change("level_id", :to => levels(:king).id) \
{ Person.find(people(:joe).id).level_id }

But I get a NoMethodError for levels.

Likewise, if I refer to assigns in the same context, I get a Nil
"while evaluating nil.assigns".

I can refer to fixtures just fine in setup and in the blocks passed to
shoulda macros, so I know it's not an inclusion problem. I gather that
the shoulda macros are evaluated before fixtures are included? How
about the lack of self in this context?

Most importantly, is there another way to accomplish this test
statement? :)

Curran

Brian Cardarella

unread,
Sep 29, 2009, 3:09:41 PM9/29/09
to shoulda

should_change is on the class level. The fixture helper is on the
instance level.

- Brian

Dan Croak

unread,
Sep 29, 2009, 3:51:43 PM9/29/09
to sho...@googlegroups.com
Hi Curran,

On Tue, Sep 29, 2009 at 2:52 PM, Curran <c.schie...@gmail.com> wrote:

> I'd like to write the following test inside a functional test
> (subclass of ActionController::TestCase):
>
>  should_change("level_id", :to => levels(:king).id) \
>    { Person.find(people(:joe).id).level_id }

Ooh, is this a game? King me!

> But I get a NoMethodError for levels.

Brian explained this.

> Most importantly, is there another way to accomplish this test
> statement? :)

We'd usually prefer to write:

should "update person" do
person = Factory(:non_king_person)
Person.stubs(:find => person)
person.stubs(:update_attributes).returns(true)

sign_in_as person

params = { 'some' => 'param' }
put :update, :id => person.to_param, :params => params

assert_received(Person, :find) { |expect| expect.with(person.to_param) }
assert_received(person, :update_attributes) { |expect| expect.with(params) }

assert_response :redirect
assert_redirected_to person_url(person)
end

The difference is you're testing that the controller calls the
expected interfaces to the model, and not that the database record is
changed. You can be confident that ActiveRecord's update_attributes
works as expected, so you don't need to actually save the record or
generally test database transactions at the functional test level.

--
Dan Croak
@Croaky

Curran Schiefelbein

unread,
Sep 29, 2009, 8:03:18 PM9/29/09
to sho...@googlegroups.com
On Tue, Sep 29, 2009 at 3:51 PM, Dan Croak <dcr...@thoughtbot.com> wrote:
The difference is you're testing that the controller calls the
expected interfaces to the model, and not that the database record is
changed. You can be confident that ActiveRecord's update_attributes
works as expected, so you don't need to actually save the record or
generally test database transactions at the functional test level.


Actually, at the moment, you can't be that confident about update_attributes! (https://rails.lighthouseapp.com/projects/8994/tickets/922-has_many-through-transaction-rollback) But you are right, I'm testing things here that are already tested in my model test.

Hadn't seen assert_received before. That makes a lot of sense.

There is a lot of crummy advice out there on how to write functional tests -- it's taken me a couple of weeks to peel back everything that really belongs in an integration test. Now I can peel out the duplication of model tests too.

Thanks very much!

Curran

Curran Schiefelbein

unread,
Oct 3, 2009, 10:17:25 PM10/3/09
to sho...@googlegroups.com
Thanks again for your patient answers. I now see this was covered just last week or thereabouts:
http://groups.google.com/group/shoulda/browse_thread/thread/f8479d112126f455

I have a question about your usual preferred version. When you write

 Person.stubs(:find => person)

Doesn't that make it difficult to write a test case involving multiple instances of people? I tried removing that line, but discovered that can make assert_received fail. The mock object "person" in the test and the object "@person" returned to the controller, by the normal Person.find, are two different objects in memory. The way Expectations work, they don't get close enough to each other for == to be relevant.

I don't really care whether a specific ActiveRecord instance received a call to update_attributes, but whether the controller called update_attributes with the right person in mind.

Based on your experience, am I barking up the wrong tree? I went sniffing around in the first place only because I discovered that

  assert_equal person, people(:joe)

passes when :find is stubbed out. (Two different objects, or they would be if Person.find weren't stubbed out)

If the answer is "don't mix stubs and fixtures", that's fine. It's not really fixtures that are problematic. Just wondering how you would write a test in which you wanted Person.find to return different mocks based on the id it receives.

Curran
Reply all
Reply to author
Forward
0 new messages