Testing your custom error pages with Webrat and Cucumber
When your application raises an error during development, Rails and Webrat will provide you with useful information like the error's type and stacktrace.
But now you created that shiny, custom error page by overriding rescue_action_in_public. You would like to write a Cucumber feature that tests whether your new error page localizes into 10 languages and displays all the information it should.
Unfortunately it requires some serious Judo to make your error page visible to selected scenarios, while keeping Rails' helpful default behavior for your other tests. Here is how you do it.
First, we will tame Webrat. Webrat raises an exception when you visit a page that shows an error. So we're going to write a step that visits a page, but ignores errors:
When /^I ignore errors while visiting (.+)$/ do |page_name|
begin
visit path_to(page_name)
rescue
end
endNow we need to get Rails to render the error page for selected scenarios. Unfortunately we will have to flip a lot of switches to accomplish this.
Put the following into your support/env.rb. With this code we can make Rails treat selected requests as remote ones, (local requests never see error pages), regardless of what consider_all_requests_local is set to:
ActionController::Base.class_eval do
protected
cattr_accessor :local_request_override
def local_request_with_override?
local_request_override.nil? ? local_request_without_override? : local_request_override
end
alias_method_chain :local_request?, :override
endFinally we need a step that flips all the magic switches for the current scenario and reverts to the previous error handling afterwards:
Given /^error pages will be rendered$/ do
@old_allow_rescue = ActionController::Base.allow_rescue
@old_consider_all_requests_local = ActionController::Base.consider_all_requests_local
@old_local_request_override = ActionController::Base.local_request_override
ActionController::Base.local_request_override = false
ActionController::Base.allow_rescue = true
ActionController::Base.consider_all_requests_local = false
end
After do
@old_allow_rescue.nil? or ActionController::Base.allow_rescue = @old_allow_rescue
@old_consider_all_requests_local.nil? or ActionController::Base.consider_all_requests_local = @old_consider_all_requests_local
@old_local_request_override.nil? or ActionController::Base.local_request_override = @old_local_request_override
endNow you can write your error_pages.feature:
Feature: custom error pages
In order to not scare my users when things go wrong
As the site owner
I want to display friendly error pages
Scenario: A country cannot be found
Given error pages will be rendered
And I ignore errors while visiting the country "Narnia"
Then I should see "Oops, we couldn't find Narnia!"I recommend you extend your feature to cover some more cases and also make it check if proper HTTP status codes are returned.
Was this post helpful to you? Then let us know!
You can follow any response to this post through the Atom feed.



Alternatively, with recent versions of cucumber, you can simply tag your scenario with @allow-rescue. With this in place, you can write your scenario just like any other scenario, and it’ll work as you’d expect.
Alex
@Alex: Thank you for the pointer to the @allow-rescue tag. You still need to make those requests not be considered local, or you will see a stack trace instead of your custom error page.
@Henning: thanks a lot! This saved quite some time. I still have some questions though.
1) Why do I have to make the requests not to be considered local? Indeed I see a stack trace, but why?
2) On another site (see ref below) I found the following that claims to achieve the same non-local effect (though I did not get it working):
header “REMOTE-ADDR”, “10.0.1.1”
Is that a sufficient replacement for your ActionControler change? Ref to the site: http://jjinux.blogspot.com/2009/08/rails-dynamic-404s-authlogic-cucumber.html
–Mike
@Mike: It’s a built-in behavior of Rails that error pages are only rendered for remote requests. Requests that are considered local always get the stacktrace.
The method on the site you mentioned requires less code than our solution, which would be preferrable. Should you find a way to get it to work, let me know.
@mike header “REMOTE-ADDRâ€, “10.0.1.1†in a “Given” clause is sufficient to spoof local_request? => false
I’ve had a similar problem - not being able to trap my custom
rescue_from ...behavior because it was wrapped in aunless consider_all_requests_localblock. When my ActionController is interpretedconsider_all_requests_localis true, before I can spoof it in cucumber’s support/env or aGiven- so therescue_fromclauses don’t apply. To fix that behavior you can either unwrap these clauses so that they work in your test environment or duplicate them like I did in support/overrides. Not pretty, but there seems to be no other way.