Specing rescue, ensure and else blocks of an Exception

Greetings,

I’m using rspec with rcov for my applications and there is one issue
which I cannot solve nor can find any proper information regarding
it: specing what is in a rescue block in case of an exception.

I’m using Ruby on Rails and I usually make use of exceptions in my
controllers, like the following example:

def action
@foo = Foo.find(1)
@foo.update_attributes!(params[:foo])
redirect_to :back
rescue ActiveRecord::RecordInvalid => e
…exception handling code …
rescue Exception => e
…exception handling code …
else
…else block
ensure
…ensure block
end

I have a specs for this, like this one:

it “should retrieve a Foo instance, identified by ID and update it’s
parameters” do
Foo.should_receive(:find).with(“1”).and_return(@foo)
@foo.should_receive(:update_attributes!).with
(valid_params).and_return(@foo)
get :action, :id => 1
response.should be_redirect
end

it “should retrieve a Foo instance, identified by ID and throw an
exception, because of bad parameters” do
Foo.should_receive(:find).with(“1”).and_return(@foo)
@foo.should_receive(:update_attributes!).with
(valid_params).and_return(@foo)
get :action, :id => 1
response.should raise_error
end

or

it “should retrieve a Foo instance, identified by ID and throw an
exception, because of bad parameters” do
Foo.should_receive(:find).with(“1”).and_return(@foo)
lambda { @foo.should_receive(:update_attributes!).with
(valid_params).and_return(@foo) }.should raise_error
get :action, :id => 1
end

… and … that’s it, the tests pass, becaue an Exception is raised,
but I don’t know how to proceed with testing all the rescue, ensure
and else blocks. Since rcov reporting it as not being run, my
coverage is suffering and I really don’t want to release code which
are only being partially tested.

So any pointers, help or links to some quick rundown regarding this
problem would be greatly appreciated.

Thanks a bunch and my best regards,
András

On 26 Oct 2007, at 11:55, Tarsoly András wrote:

So any pointers, help or links to some quick rundown regarding this
problem would be greatly appreciated.

From a brief look over (apologies if I missed something), you are
not invoking the errors you need to cause your rescue etc blocks to run.

I think you want to do:
@foo.should_receive(:update_attributes!).with(params
[:foo]).and_raise(RecordNotSaved)
@object_in_rescue_block.should_receive(:some_message)
get :action, :id => 1

What you appear to be doing here:

it “should retrieve a Foo instance, identified by ID and throw an
exception, because of bad parameters” do
Foo.should_receive(:find).with(“1”).and_return(@foo)
lambda { @foo.should_receive(:update_attributes!).with
(valid_params).and_return(@foo) }.should raise_error
get :action, :id => 1
end

is testing that the code throws an exception, NOT that your code
handles it; ie you are describing an assumption of someone else’s
code rather than the behaviour of yours, which is not what you want
in a spec.

Ashley

blog @ http://aviewfromafar.net/
linked-in @ http://www.linkedin.com/in/ashleymoran
currently @ work

On 10/26/07, Tarsoly András [email protected] wrote:

@foo = Foo.find(1)
end
end
or

it “should retrieve a Foo instance, identified by ID and throw an exception,
because of bad parameters” do
Foo.should_receive(:find).with(“1”).and_return(@foo)
lambda {
@foo.should_receive(:update_attributes!).with(valid_params).and_return(@foo)
}.should raise_error
get :action, :id => 1
end

There are a few problems with these examples. First of all, they use
“get” when they should use “post”, so they’re likely not executing the
right stuff.

Next, they are describing expected model behaviour in your controller
spec. What we’re interested in here is not that the model raises an
error under certain conditions - that’s something we should spec in
model specs - we’re interested in what the controller does when the
model raises an error.

Lastly, the examples have AND in them. That should be a red flag. Each
example should describe a single aspect of the behaviour of the action
when invoked under specific circumstances.

Take a look at Parked at Loopia (reorganized from your
example). Note that each example is about what the controller does,
not what the model does. Also note that there are no "and"s in any of
the labels. Each example sets up one thing and describes what happens
when the action is invoked based on those conditions.

HTH,
David

On 10/26/07, David C. [email protected] wrote:

On 10/26/07, Tarsoly András [email protected] wrote:
Take a look at Parked at Loopia (reorganized from your
example). Note that each example is about what the controller does,
not what the model does. Also note that there are no "and"s in any of
the labels. Each example sets up one thing and describes what happens
when the action is invoked based on those conditions.

HTH,
David

Here’s an alternative broken down into more granular specs:
Parked at Loopia.

Generally I find that I don’t break up my controller specs like that,
but I break up specs for models like that all the time, so maybe I
should be doing them this way :slight_smile:

Thanks a lot guys, this is great help, it’s really appreciated, it
makes a lot more sense now.

I somehow felt that I’m not entirely on the right path when I’m doing
controller testing, and your replies and examples given here shed
some light on it.

David, I like your alternative better, since it has more structure.

Also, I’d like to ask one more question, which popped into my mind
after reviewing these examples, but it’s not relating strictly into
the previous topic.

Is there some way, to say something like:

it “should” do
post :any_action, params
end

I’m using some before filters in my controllers to DRY up my code,
for example: retrieve a specific record, if params[:id] is set. This
is very useful for automatic scoping and assigning instance vars in
some controllers, which are relate to nested resources (eg.
map.resources :foo, has_many => [:bar])

The aim of this would be to DRY up the tests a little bit, since each
action always retrieves a specific set of records, and I could spec
it out in the shared model for all possible actions at once, or I am
missing something here either? :slight_smile:

Thanks again for the great examples, regards:
András

On 2007.10.26., at 14:02, David C. wrote:

Here’s an alternative broken down into more granular specs:
Parked at Loopia.

Generally I find that I don’t break up my controller specs like that,
but I break up specs for models like that all the time, so maybe I
should be doing them this way :slight_smile:


rspec-users mailing list
[email protected]
http://rubyforge.org/mailman/listinfo/rspec-users


Tarsoly
Andrá[email protected]

On 26 Oct 2007, at 12:56, Ashley M. wrote:

@foo.should_receive(:update_attributes!).with(params
[:foo]).and_raise(RecordNotSaved)

Sorry, RecordInvalid.new in your case, not RecordNotSaved (with or
without the new)

blog @ http://aviewfromafar.net/
linked-in @ http://www.linkedin.com/in/ashleymoran
currently @ work