I am building a JSON backend API with Rails, and I can't seem to figure this out: How can one test using RSpec the Rails default error responses? For example, for a generic 404 request, I currently have the following request spec:
require 'rails_helper'
RSpec.describe '404 errors' do
let(:json) { JSON.parse(response.body) }
before { get '/noexistent', headers: headers }
# examples
end
Rails, however, refuses to render the 404 response that it would normally yield, instead simply raising:
ActionController::RoutingError:
No route matches [GET] "/404"
I've tried removing config.consider_all_request_local = true from config/environments/test.rb, with no results.
Note that I've seen this question (it's where I got the idea for consider_all_requests_local), but it applies to development, not testing.
EDIT: I am using config.exceptions_app = self.routes to redirect errors to an ErrorsController. This works fine in development or production, but not while testing. When testing, I still get the above error. Any ideas?
It turns out the answer was rather simple. I had to change this line in config/environments/test.rb:
# Raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = false
to:
# Do not raise exceptions instead of rendering exception templates.
config.action_dispatch.show_exceptions = true
Related
I'm working on a Rails 5 JSON API, and I've noticed that it's providing sensible exception-handling out of the box.
For example, ActiveRecord::RecordNotFound results in a 404, ActionController::ParameterMissing results in 400, and these are all returned as well-formed JSON responses.
I've found plenty of documentation on rescue_from, can't locate what's providing this exception-handling for me.
I've dug around in the Rails guides, the docs, as well as in the rails console, e.g.
ActionController::API.new.rescue_handlers
=> []
ApplicationController.new.rescue_handlers
=> []
I'd like to know where the exception-handling is coming from because I've noticed that it's gracefully applied for real requests, but not in controller specs.
For example, if I run a local server and send it a malformed request with curl, I get a sensible error response. But if I try to reproduce that in a controller spec,
it "returns 404 if Yogurt doesn't exist" do
put :update, params: { id: 293459 }
end
the test throws the raw exception (ActiveRecord::RecordNotFound in this case).
When you set up a Rails 5 app with the --api option you end up using ActionController::API. https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/api.rb
The modules are built here: https://github.com/rails/rails/blob/a0061d2389a178b093f0d3f64f58236ffbe088e0/actionpack/lib/action_controller/api.rb#L104 so that's where your responses are coming ultimately from. Looks like the rendering is being handed off to metal at some point.
API actions are configured to return :json by default, so that's why your test is behaving differently than your browser.
For your spec to work the same way you'll have to supply a format:
put :update, params: { id: 293459 }, format: :json
I have a Rails 4.2 application....I was adding content compression via this thoughtbot blog post, but I get an error such as:
undefined method `get' for #<RSpec::ExampleGroups::Compression:0x00000009aa4cc8>
Perusing over the capybara docs, it seems like you shouldn't be using get. Any idea how to test the below then in Rails 4?
# spec/integration/compression_spec.rb
require 'spec_helper'
feature 'Compression' do
scenario "a visitor has a browser that supports compression" do
['deflate','gzip', 'deflate,gzip','gzip,deflate'].each do|compression_method|
get root_path, {}, {'HTTP_ACCEPT_ENCODING' => compression_method }
response.headers['Content-Encoding'].should be
end
end
scenario "a visitor's browser does not support compression" do
get root_path
response.headers['Content-Encoding'].should_not be
end
end
In a capybara test you would use visit not get (as described here), but that answer won't actually help you because the test you've written above is not an integration test, it's a controller test.
Move it to spec/controllers and use the controller-specific helpers describe/context/it etc. to construct your tests for your controller. You can set the headers and do the sorts of checks that you're doing in the code you're showing.
having a weird issue with Rails (3.2.15)
I messed around with nicer error pages, but as that was suppressing ALL errors at one point I needed to revert so I can see errors in the log files.
I had:
installed gem: 'exception_notification'
added initialisation code for that in the relevant environment file (config.middleware...)
added routes to catch errors 'get '/500', :to => 'errors#server_error' ', same for 404/422
added rescue statements in application controller "rescue_from Exception, with: lambda { |exception| render_error 500, exception }" etc, same for 404 and render method to render custom pages
Now, I didn't want any of that any more as stated, so I have removed all of the above, ran bundle install, etc. and deployed to my staging environment.
However, NOW I get blank pages on any errors (500 or 404), I don't see that standard rails 'Something went wrong page' at all, (though it is still in /public)
I don't understand why I'm not back at the default error handling, I have no 'rescue' statements anywhere, and no routes that mess with errors.
The log file on 500 shows the full error that occurs and then just:
rendered [page I was trying to see] within layouts/application (50.4ms)
"Completed 500 Internal Server Error in 184.9ms"
[error details]
Why am I not getting my default Rails error behaviour, I'm not sure what I'm missing here??
Any suggestions greatly appreciated!!
turned out I had forgotten to remove this line in config/application.rb :
config.exceptions_app = self.routes
since I removed any routes to do with errors it was therefore showing blank pages.
d'oh
So, I followed José Valim's blogpost, adding
config.exceptions_app = self.routes
to my application.rb for routing HTTP errors to my own controller.
Everything works just as it's supposed to, my custom error pages work and such, but it throws an ActionController::RoutingErrorthough, which gets logged as FATAL. Is there anyway to get around that FATALmessage? I've been trying to rescue from the error both in the application controller and in my error controller, but to no avail.
2012-06-27 12:19:33.908 [FATAL] [127.0.0.1]
ActionController::RoutingError (No route matches [GET] "/3489423u4023ho")
Did you add a route like match "/404", :to => "errors#not_found"?
Are you using the production mode, or the development mode?
You won’t be able to see your custom exceptions in development unless you set config.consider_all_requests_local to false in your config/environments/development.rb. The reason is, if the request is considered local, Rails will always favor to show the debug exceptions page;
Using Devise for authentication. On a controller that has:
before_filter authenticate_user!, :except => [ :index, :show ]
I always get 304 Not Modified status code instead of 200 OK on the authenticated actions, even in the browser while signed in. The views render and work just fine.
It's stopping my tests from passing:
describe 'GET index' do
it 'should be successful' do
get 'index'
response.should be_success # Fails due to 304 status code
end
end
I thought it was my controller's fault at first, but besides the before_filter and decent_exposure, the controller couldn't be any more common.
What could possibly be the root of this issue?
304s are a good thing. In this case, it is what is expected (and desired) even though it may be giving some of your tests trouble. A 304 means that your webserver and client are communicating in a way to allow caching of the webserver's response.
I'm not entirely familiar with Rails, but I'd suspect there is a built in mechanism which is caching your responses. Here is a Rails article on caching:
http://guides.rubyonrails.org/caching_with_rails.html
And here is what looks like a way to disable caching on the Controller/Action level (ignore the parts about iframes... also this might not be the best way):
http://arjunghosh.wordpress.com/2008/04/29/how-to-force-the-browser-to-not-cache-in-rails/
The tests were failing because I was using Devise for authentication with the confirmable module and was not using confirmed users.
After setting the confirmed_at attribute in the factory, all tests passed.