Why does this namespaced route fail? - ruby-on-rails

Here's the route:
namespace :admin do
get 'statistics', to: 'dashboard#statistics', as: :statistics
end
Here's the routing spec:
it 'routes to #statistics' do
expect(get: '/admin/statistics').to route_to 'admin/dashboard#statistics'
end
It passes perfectly.
However, this controller spec, that uses the above route, fails:
RSpec.describe Admin::DashboardController, :type => :controller do
let(:user){ FactoryGirl.create :user }
let(:admin){ FactoryGirl.create :admin }
describe '#statistics:' do
let(:request){ get :statisitcs }
context 'When guest;' do
before { request }
describe 'response' do
subject { response }
its(:status){ should eq 302 }
its(:content_type){ should eq 'text/html' }
it{ should redirect_to 'new' }
end
end
end
end
The problem is:
1) Admin::DashboardController#statistics: When admin; response content_type
Failure/Error: let(:request){ get :statisitcs }
ActionController::UrlGenerationError:
No route matches {:action=>"statisitcs", :controller=>"admin/dashboard"}
But doesn't the routing spec prove that such a route exists?

Looks like your request is misspelled in the controller spec?
let(:request){ get :statisitcs }
Should be
let(:request){ get :statistics }
Based on the request spec and the route definition. Its showing up as misspelled in the failed test as well, so...

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

Nested API : ActionController::UrlGenerationError - No route matches

I want to test my API with RSpec.
Api::V1::EventsController exists and has a create method. I use simple_token_authentication and pundit for security.
routes.rb
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
# users/
resources :users, only: [:show, :update] do
# users/:id/events/
resources :events
end
end
end
end
Spec :
RSpec.describe Api::V1::EventsController, type: :controller do
describe 'events#create' do
before {
#user = User.create(email: 'm#m.fr', password: '12345678', password_confirmation: '12345678')
#user.reload
}
it 'should 401 if bad credentials' do
# Given the user
# When
post "/api/v1/users/#{#user.id}/events", {},
{
'x-user-email' => 'toto',
'x-user-token' => 'toto'
}
# Then
expect_status 401
end
end
end
And i get this error :
Failure/Error: post "/api/v1/users/#{#user.id}/events", {},
ActionController::UrlGenerationError:
No route matches {:action=>"/api/v1/users/1/events", :controller=>"api/v1/events"}
EDIT and answer :
I was confused and i was using rspec controller when i wanted to use rspec request.
Here's my working example :
RSpec.describe Api::V1::EventsController, type: :controller do
describe 'events#create' do
before {
#user = User.create(email: 'm#m.fr', password: '12345678', password_confirmation: '12345678')
#user.reload
}
it 'should 401 if bad credentials' do
# Given the user
# When
post "/api/v1/users/#{#user.id}/events", {}.to_json,
{
'Accept' => 'application/json',
'Content-Type' => 'application/json',
'x-user-email' => 'toto',
'x-user-token' => 'toto'
}
# Then
expect_status 401
end
end
end
The post method in a controller spec takes an action name for the first parameter, not a path, so instead of:
post "/api/v1/users/#{#user.id}/events", #...
try:
post :create, #...
Controller specs are unit tests. If you want to test the entire stack, use a feature spec instead of a controller spec.
You don't need to do the request with the full path, because you already testing the Api::V1::EventsController.
So it will be much better to use special syntax for it:
post :create, nil, {
'x-user-email' => 'toto',
'x-user-token' => 'toto'
}
expect(response.response_code).to eq 401
If you want to test the route, you should do it in the routes specs:
# spec/routing/api_v1_events_routing_spec.rb
require "spec_helper"
RSpec.describe Api::V1::EventsController do
describe "routing" do
it "#create" do
expect(post: "/api/v1/users/1/events").to \
route_to(controller: "api/v1/events", action: "create", user_id: "1")
end
end
end

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 controller subject instance_variables

I have a controller and tests it through rspec:
describe "GET 'index'" do
subject { get :index }
it { expect(subject).to render_template(:index) }
My controller generates instance variables passed to views, smth. like that:
#specifications = current_user.specifications
How can I test that controller pass instance variables correct?
Something like that:
it { expect(subject).assign(:contractors).to match_array(my_array) }
You can use controller helper test method
describe TetsController do
let(:user) { build_stubbed :user }
before do
controller.stub authenticate_user!: true,
current_user: user
end
describe 'GET index' do
let(:plans) { double :plans }
before do
expect(Plan).to receive(:all).and_return(plans)
end
it 'response success' do
get :index
expect(response).to be_success
end
it 'assign plans' do
get :index
expect(assigns(:plans)).to eq plans
end
end
end
Small example. controller has instance variable #plans. It's accessed as assigns(:plans)

Rspec namespaced controllers fails

I want to test my namespaced controllers but when I raise controller nothing raised and test is passing. I get no routes error. They all work fine. But it must raise error. Actually it must call index action but it does not. What is the cause?
dashboard_controller_spec.rb
require 'spec_helper'
describe Admin::DashboardController do
it "gets index" do
get :index
end
end
routes.rb
namespace :admin do
match 'dashboard' => 'dashboard#index', :as => :dashboard
end
dashboard_controller.rb
class Admin::DashboardController < Admin::ApplicationController
def index
raise "asd"
end
end
Hi you can write the route path as follow so get index action
describe :route do
subject { {get: "/Admin/dashboardes"} }
it { should route_to(controller: "Admin/dashboardes", action: "index") }
end
like example :
describe "#index" do
describe :route do
subject { {get: "/administration/users"} }
it { should route_to(controller: "administration/users", action: "index") }
end
end

Resources