RSpec 404 test respond with status 200 - ruby-on-rails

I've written a test to test the 404 page
pages_controller_spec.rb
RSpec.describe PagesController, type: :controller do
before :all do
Rails.application.config.action_dispatch.show_exceptions = true
Rails.application.config.consider_all_request_local = false
end
describe "status 404" do
it "respond with 404 if page is not found" do
get :help, params: { id: "foobar" }
expect(response.status).to eq(404)
end
end
end
The pages controller is simple and functions to render the static pages "/help" and "/about"
class PagesController < ApplicationController
def help
end
def about
end
end
The error handling is set up as follows
application_controller.rb
def not_found
raise ActionController::RoutingError.new("Not Found")
rescue
render_404
end
def render_404
render file: "#{Rails.root}/public/404", status: :not_found
end
The test result is
expected: 404
got: 200
Which I do not understand since "/help/foobar" does render the 404 page when I try it myself in the browser. I guess the problem could be that my "get" action in the test is formatted wrong but I'm not sure.
EDIT
config/routes.rb as requested
get "/about", to: "pages#about"
get "/help", to: "pages#help"
EDIT 2
Updated the test with the syntax from https://relishapp.com/rspec/rspec-rails/v/3-4/docs/routing-specs/route-to-matcher
The test now looks like this
RSpec.describe PagesController, type: :controller do
before :all do
Rails.application.config.action_dispatch.show_exceptions = true
Rails.application.config.consider_all_request_local = false
end
describe "status 404" do
it "respond with 404 if page is not found" do
expect(get("/foobar")).to route_to("application#not_found")
end
end
end
Unfortuenly this raises another error
ActionController::UrlGenerationError:
No route matches {:action=>"/foobar", :controller=>"pages"}
No route is matching which is kind of the point but the "not_found" method is being used for some reason

Change your routes to make your call work from the browser:
get "/help/:id", to: "pages#help"
If the test returns a 200, it's because it calls directly the help method from your controller without using the config/routes.rb file.
EDIT
Here is how you test your routing: https://relishapp.com/rspec/rspec-rails/v/3-4/docs/routing-specs/route-to-matcher

Related

How to test a customized not_found route in rails

I have the following situation:
EDITED
In my routes.rb
namespace :api, defaults: { format: :json } do
namespace :v1 do
# the definitions of other routes of my api
# ...
match '*path', to: 'unmatch_route#not_found', via: :all
end
end
EDITED
My controller:
class Api::V1::UnmatchRouteController < Api::V1::ApiController
def not_found
respond_to do |format|
format.json { render json: { error: 'not_found' }, status: 404 }
end
end
end
My test is as shown:
require 'rails_helper'
RSpec.describe Api::V1::UnmatchRouteController, type: :controller do
describe 'get response from unmatched route' do
before do
get :not_found, format: :json
end
it 'responds with 404 status' do
expect(response.status).to eq(404)
end
it 'check the json response' do
expect(response.body).to eq('{"error": "not_found"}')
end
end
end
It seems right to me, however I got the same error for both it statments:
1) Api::V1::UnmatchRouteController get response from unmatched route responds with 404 status
Failure/Error: get :not_found, format: :json
ActionController::UrlGenerationError:
No route matches {:action=>"not_found", :controller=>"api/v1/unmatch_route", :format=>:json}
# /home/hohenheim/.rvm/gems/ruby-2.3.1#dpms-kaefer/gems/gon-6.1.0/lib/gon/spec_helpers.rb:15:in `process'
# ./spec/controllers/api/v1/unmatch_route_controller_spec.rb:14:in `block (3 levels) in <top (required)>'
EDITED
The purpose with this route is be trigged when there's no other route possible in my api, with a custom json 404 response. This route and controller is working as expected right now, when we access routes like: /api/v1/foo or /api/v1/bar
How can I write the tests properly?
Additional info: Rails 4.2.6, Rspec 3.5.4
If you try to write routes spec, it won't work too and it will return something strange.
Failure/Error:
expect(get("/unmatch")).
to route_to("unmatch_route#not_found")
The recognized options <{"controller"=>"unmatch_route", "action"=>"not_found", "path"=>"unmatch"}> did not match <{"controller"=>"unmatch_route", "action"=>"not_found"}>, difference:.
--- expected
+++ actual
## -1 +1 ##
-{"controller"=>"unmatch_route", "action"=>"not_found"}
+{"controller"=>"unmatch_route", "action"=>"not_found", "path"=>"unmatch"}
Beside action not_found, it returned path => unmatch that maybe why controller spec didn't work as expected. Thus instead of controller test you can use request test as below.
require 'rails_helper'
RSpec.describe "get response from unmatched route", :type => :request do
before do
get '/not_found', format: :json
end
it 'responds with 404 status' do
expect(response.status).to eq(404)
end
it 'check the json response' do
expect(response.body).to eq('{"error": "not_found"}')
end
end
Take a look at this link:
https://apidock.com/rails/ActionDispatch/Routing/Mapper/Base/match
It says:
Note that :controller, :action and :id are interpreted as url query parameters and thus available through params in an action.
match ":controller/:action/:id"
Your route is:
match '*path', to: 'unmatch_route#not_found', via: :all
So your test is trying to find a route with :action=>"not_found" inside :controller=>"api/v1/unmatch_route". But your routes.rb does not have this route.
try something like this:
match 'unmatch_route/not_found', to: 'unmatch_route#not_found', via: :all
If you really need to use *path try this:
match '/:path/', :to => 'unmatch_route#not_found', :path=> /.*/, :as =>'not_found'
I also found myself wanting to test the response for API errors was rendering JSON, rather than writing a spec which simply rescued ActionController::RoutingError.
The following request spec worked for me, using Rails 6.0 & RSpec 3.9:
require 'rails_helper'
RSpec.describe '404 response for API endpoints' do
it 'renders an error in JSON' do
render_exceptions do
get '/api/v1/fictional-endpoint', headers: { 'Accept' => 'application/json' }
end
expect(response).to have_http_status(:not_found)
expect(response['Content-Type']).to include('application/json')
expect(json_response.fetch(:errors)).to include('Not found')
end
private
def json_response
JSON.parse(response.body, symbolize_names: true)
end
def render_exceptions
env_config = Rails.application.env_config
original_show_exceptions = env_config['action_dispatch.show_exceptions']
original_show_detailed_exceptions = env_config['action_dispatch.show_detailed_exceptions']
env_config['action_dispatch.show_exceptions'] = true
env_config['action_dispatch.show_detailed_exceptions'] = false
yield
ensure
env_config['action_dispatch.show_exceptions'] = original_show_exceptions
env_config['action_dispatch.show_detailed_exceptions'] = original_show_detailed_exceptions
end
end
References:
How to have Rails request specs handling errors like production
Comment regarding Rails.application.env_config caching

Routes appear to exist in rake routes, unavailable when running spec

I cannot seem to get the following worked out.
Spec: (spec/api/power_ups_spec.rb)
describe Api::PowerUpsController, :type => :controller do
describe "GET power_ups" do
it "returns all power-ups" do
FactoryGirl.create :power_up, name: "Increase rate of take", description: "You gain points more quickly"
FactoryGirl.create :power_up, name: "Decrease rate of give", description: "You lose points more slowly"
get api_power_ups_path, {}, { "Accept" => "application/json" }
expect(response.status).to eq 200
body = JSON.parse(response.body)
power_up_names = body.map { |m| m["title"] }
expect(power_up_names).to match_array(["Increase rate of take",
"Decrease rate of give"])
end
end
end
Routes:
Rails.application.routes.draw do
namespace :api do
resources :power_ups, only: [:index]
end
end
Controller (app/controllers/api/power_up_controller.rb):
module Api
class PowerUpsController < ApplicationController
include ActionController::MimeResponds
respond_to :json
def index
respond_with PowerUp.all
end
end
end
Rake Routes:
Prefix Verb URI Pattern Controller#Action
api_power_ups GET /api/power_ups(.:format) api/power_ups#index
Error message on running spec:
Failure/Error: get api_power_ups_path, {}, { "Accept" => "application/json" }
ActionController::UrlGenerationError:
No route matches {:action=>"/api/power_ups", :controller=>"api/power_ups"}
get api_power_ups_path
this isn't how to use get in a controller spec.
In a controller-spec, you assume that the class-under-test in the controller... so you use get to call the actual method on the controller.
In this case the method is called index (ie, you have def index), so to activate the test you just call:
get :index
you use the path-helpers only when you are referring to other paths - eg where you get redirected to etc.

Rspec: how do I test for a redirect?

I've got some routes that look like this:
constraints ClientCodeConstraint.new do
get 'share/:client_code', to: redirect('/%{client_code}')
get 'share/:client_code/:id', to: redirect('/%{client_code}/%{id}')
get ':client_code', to: 'share#index', as: :shares
get ':client_code/:id', to: 'share#show', as: :share
get ':client_code/:document_id/more/:component_id', to: 'share#more', as: :more
end
My constraint looks like this:
class ClientCodeConstraint
def matches?(request)
users.each do |user|
return true if request.path_parameters[:client_code] == user.client_code
end
end
def users
#users ||= User.all
end
end
I've got tests like this but I'm having no luck:
context 'not logged in' do
let!(:user) { create(:user) }
let!(:document) { create(:document) }
specify 'renders the :index view' do
get :index
expect(response.code).to eq '302'
expect(response).to redirect_to("/#{assigns(:user).client_code}")
end
specify 'renders the :show view' do
get :show
expect(response).to redirect_to("/#{assigns(:user).client_code}/#{assigns(:document).id}")
end
end
How do I test with Rspec these redirects? I just get a bunch of these errors:
ActionController::RoutingError: No route matches {:controller=>"share"}
And the controller and routes of course exist.
UPDATE
Relevant rake routes output is here:
GET /share/:client_code(.:format) :controller#:action
GET /share/:client_code/:id(.:format) :controller#:action
shares GET /:client_code(.:format) share#index
share GET /:client_code/:id(.:format) share#show
more GET /:client_code/:document_id/more/:component_id(.:format) share#more
The tests pass like this:
specify 'renders the :index view' do
get :index, client_code: user.client_code
expect(response.status).to eq(200)
end
That feels though like I'm testing only that the response is correct. I want to test more thoroughly, i.e., it doesn't render an error message, etc.

Ruby on Rails RSpec Routing fail

Rails newb here.
Trying to RSpec test a 200 status code for an index route.
In my index_controller_spec.rb:
require 'spec_helper'
describe IndexController do
it "should return a 200 status code" do
get root_path
response.status.should be(200)
end
end
routes.rb:
Tat::Application.routes.draw do
root to: "index#page"
end
index_controller:
class IndexController < ApplicationController
def page
end
end
When I visit on my browser all is fine but RSpec command line gives an error:
IndexController should return a 200 status code
Failure/Error: get '/'
ActionController::RoutingError:
No route matches {:controller=>"index", :action=>"/"}
# ./spec/controllers/index_controller_spec.rb:6:in `block (2 levels) in <top (required)>
'
I don't understand?!
Thanks.
Welcome to the Rails world! Testing comes in many different flavors. It appears that you're confusing a controller test with a routing test.
You're seeing this error because root_path is returning /. The get :action within an RSpec controller test is meant to call that method on that controller.
If you notice your error message, it says :action => '/'
To test your controller, change your test to:
require 'spec_helper'
describe IndexController do
it "should return a 200 status code" do
get :page
response.status.should be(200)
end
end
If you're interested in a routing test, see https://www.relishapp.com/rspec/rspec-rails/docs/routing-specs An example would be:
{ :get => "/" }.
should route_to(
:controller => "index",
:action => "page"
)

Trace Rspec's get request routing in controller action spec

I am trying to test a simple controller action in a moduled controller. However, my get :index request returns a 404, instead of a 200 response. Is there a way to trace the routing of this get request?
require "spec_helper"
describe Admin::WidgetsController do
describe "GET index" do
it "has a 200 status code" do
get :index
response.code.should eq("200")
end
end
end
The controller looks like as you would expect:
class Admin::WidgetsController < Admin::ApplicationController
respond_to :html, :xml, :json
def index
respond_with(#content = "content")
end
end
Sounds like something is wrong with your routing. On the console you can run this to see what routes are available to your app:
$> rake routes
I'm pretty sure the following, when it fails, will show you what it's being redirected to
describe Admin::WidgetsController do
describe "GET index" do
it "has a 200 status code" do
get :index
response.should redirect_to(:action => 'other_action')
end
end
end
You can check out these links for more info:
http://guides.rubyonrails.org/routing.html
http://old.rspec.info/rails/writing/controllers.html

Resources