RSpec RoutingError for nested controller - ruby-on-rails

I'm testing a nested controller and get the following error:
1) Checklists::ItemsController index action should render index template
Failure/Error: get :index, :checklist_id => checklist.id
ActionController::RoutingError:
No route matches {:checklist_id=>1, :controller=>"checklists/items"}
In the browser loading /checklists/1/items loads fine.
Am I missing something in the spec?
The routes:
resources :checklists do
resources :items, :controller => "Checklists::Items"
end
The controller located in namespaced folder (/app/controllers/checklists/items_controller.rb):
class Checklists::ItemsController < ApplicationController
respond_to :html, :json
def index
#checklist_items = #checklist.items
respond_with #checklist_items
end
end
The spec (/spec/controllers/checklists/items_controller_spec.rb):
describe Checklists::ItemsController do
let(:user) { Factory :user, :role => 'admin' }
let(:checklist) { Factory(:checklist) }
let(:checklist_item) { Factory(:checklist_item) }
before(:each) do
sign_in_to(controller, user)
Checklist.stub(:find => checklist)
end
it "index action should render index template" do
get :index, :checklist_id => checklist.id
response.should render_template(:index)
end
end
Update: Routes for checklist items
checklist_items GET /checklists/:checklist_id/items(.:format) {:action=>"index", :controller=>"Checklists::Items"}
POST /checklists/:checklist_id/items(.:format) {:action=>"create", :controller=>"Checklists::Items"}
new_checklist_item GET /checklists/:checklist_id/items/new(.:format) {:action=>"new", :controller=>"Checklists::Items"}
edit_checklist_item GET /checklists/:checklist_id/items/:id/edit(.:format) {:action=>"edit", :controller=>"Checklists::Items"}
checklist_item GET /checklists/:checklist_id/items/:id(.:format) {:action=>"show", :controller=>"Checklists::Items"}
PUT /checklists/:checklist_id/items/:id(.:format) {:action=>"update", :controller=>"Checklists::Items"}
DELETE /checklists/:checklist_id/items/:id(.:format) {:action=>"destroy", :controller=>"Checklists::Items"}

It turns out the solution to the problem was in the routes:
I changed
resources :items, :controller => "Checklists::Items"
to
resources :items, :controller => "checklists/items"
and it works now

Related

How to replace 'show' with 'download' in rails routes?

This is driving me crazy. Rails is saying I don't have a controller action defined, but I clearly do in rake routes.
What am I missing here?
spec/controllers/api/v1/files_controller_spec.rb:
describe "download action" do
after do
get :download, id: file.id
end
it "returns http status 200 OK" do
expect(response).to have_http_status(200)
end
end
Failure message of no route matches action download:
Failures:
1) Api::V1::FilesController download action returns http status 200 OK
Failure/Error: get :download, id: file.id
ActionController::UrlGenerationError:
No route matches {:action=>"download", :controller=>"api/v1/files", :id=>"2"}
config/routes.rb:
namespace :api, defaults: { format: :json } do
namespace :v1 do
resources :files, only: [:index, :create]
get "files/:id", to: "file#download", as: "file"
end
end
rake routes | grep file:
api_v1_files GET /api/v1/files(.:format) api/v1/files#index {:format=>:json}
POST /api/v1/files(.:format) api/v1/files#create {:format=>:json}
api_v1_file GET /api/v1/files/:id(.:format) api/v1/file#download {:format=>:json}
You can add more routes to a resource with a block like so:
resources :files, only: [:index, :create] do
member do
get 'download'
end
end

ActionController::UrlGenerationError - with route defined and action in controller, still getting error for no route

I'm getting the following error in RSpec when running my schools_controller_spec.rb test:
ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"schools"}
What's puzzling me is that I have the routes configured, and the action defined in the appropriate controller. I'm not getting this error for other tests in the spec, such as 'GET #index', etc. Running Rails 4.2 with RSpec/Capybara.
Here's the routes.rb:
Rails.application.routes.draw do
root to: 'pages#home', id: 'home'
resources :users
resources :schools
resource :session, only: [:new, :create, :destroy]
match '/home', to: 'pages#home', via: 'get', as: 'home_page'
end
rake routes returns:
schools GET /schools(.:format) schools#index
POST /schools(.:format) schools#create
new_school GET /schools/new(.:format) schools#new
edit_school GET /schools/:id/edit(.:format) schools#edit
school GET /schools/:id(.:format) schools#show
PATCH /schools/:id(.:format) schools#update
PUT /schools/:id(.:format) schools#update
DELETE /schools/:id(.:format) schools#destroy
There's the route defined on the fifth line, as schools#show.
The schools_controller.rb:
class SchoolsController < ApplicationController
before_action :require_signin
before_filter :admin_only, except: :index, :show
def index
#schools = School.all
end
def show
# code pending
end
private
def admin_only
unless current_user.admin?
redirect_to :back, alert: "Access denied."
end
end
end
The link to the individual school seems to be properly defined in the view helper (_school.html.haml):
%li#schools
= link_to school.name, school
= school.short_name
= school.city
= school.state
and looking at the front-end HTML confirms it's working correctly. I can see, for example: Community College of the Air Force. When I click that link the page shows the following in the debug dump:
--- !ruby/hash:ActionController::Parameters
controller: schools
action: show
id: '1'
Finally, for good measure, here's the spec file (schools_controller_spec.rb):
require 'rails_helper'
describe SchoolsController, type: :controller do
# specs omitted for other actions
describe 'GET #show' do
context "when not signed in" do
it "returns a 302 redirect code" do
get :show
expect(response.status).to eq 302
end
it "redirects to the signin page" do
get :show
expect(response).to redirect_to new_session_path
end
end
context "when signed in as user" do
before :each do
#user = double(:user)
allow(controller).to receive(:current_user).and_return #user
#school = create(:school)
end
it "assigns the school to the #school variable" do
get :show
expect(assigns(:school)).to eq #school
end
end
end
end
The route appears in rake routes. The method is defined in the appropriate controller. There don't appear to be any silly naming errors (e.g. plural/singular). The spec doesn't appear to have any issues routing GET #index or other routes for example. Everything works exactly as expected in the browser.
So why do I keep getting the "no route matches" error when I run my controller spec?
This is because the show action is expecting an id which you currently aren't passing. Replace:
get :show
With this:
get :show, id: school.id
The above assumes you have a school variable, perhaps a let in a before block?
let(:school) { create(:school) }

How to test Controllers under different namespaces and why this test fails?

Dear fellow Overflowers,
I am using Rails 2.3 and I have created a polymorphic Controller which is accessed by Views belonging to different namespaces. Here is the story and thanks for reading it in advance:
I have these routes:
rake routes | grep appointment
new_patient_appointments GET /patients/:patient_id/appointments/new(.:format) {:controller=>"appointments", :action=>"new"}
edit_patient_appointments GET /patients/:patient_id/appointments/edit(.:format) {:controller=>"appointments", :action=>"edit"}
patient_appointments GET /patients/:patient_id/appointments(.:format) {:controller=>"appointments", :action=>"show"}
PUT /patients/:patient_id/appointments(.:format) {:controller=>"appointments", :action=>"update"}
DELETE /patients/:patient_id/appointments(.:format) {:controller=>"appointments", :action=>"destroy"}
POST /patients/:patient_id/appointments(.:format) {:controller=>"appointments", :action=>"create"}
new_admin_doctor_appointments GET /admin/doctors/:doctor_id/appointments/new(.:format) {:controller=>"admin/appointments", :action=>"new"}
edit_admin_doctor_appointments GET /admin/doctors/:doctor_id/appointments/edit(.:format){:controller=>"admin/appointments", :action=>"edit"}
admin_doctor_appointments GET /admin/doctors/:doctor_id/appointments(.:format) {:controller=>"admin/appointments", :action=>"show"}
PUT /admin/doctors/:doctor_id/appointments(.:format) {:controller=>"admin/appointments", :action=>"update"}
DELETE /admin/doctors/:doctor_id/appointments(.:format) {:controller=>"admin/appointments", :action=>"destroy"}
POST /admin/doctors/:doctor_id/appointments(.:format) {:controller=>"admin/appointments", :action=>"create"}
...these controllers:
Controllers/Admin/doctors_controller.rb
class Admin::DoctorsController < AuthorisedController
end
Controllers/appointments_controller.rb
class AppointmentsController < ApplicationController
end
Controllers/patients_controller.rb
class PatientsController < ApplicationController
end
...and these tests:
The relevant part in the tests:
test/functional/appointments_conrtroller_test.rb
require 'test_helper'
class AppointmentsControllerTest < ActionController::TestCase
fixtures :patients, :appointments, :doctors, :users
# The following passes:
def setup
login_as :admin
end
test "should show patient appointment" do
get :show, :id => patients(:one).to_param, :appointment_id => appointments(:app_one).id
assert_response :success
end
# The following fails, giving the error after the code block:
test "should show doctor appointment" do
get :show, :id => doctors(:one).to_param, :appointment_id => appointments(:app_one).id
assert_response :success
end
end
Error:
4) Error:
test_should_show_doctor_appointment(AppointmentsControllerTest):
ActionController::RoutingError: No route matches {:controller=>"appointments", :id=>"281110143", :action=>"show", :doctor_id=>2}
test/functional/appointments_controller_test.rb:55:in `test_should_show_doctor_appointment'
the test is under the base namespace, so as a next step, I created a test under Admin.
test/functional/admin/appointments_controller_test.rb
class Admin::AppointmentsControllerTest < ActionController::TestCase
fixtures :patients, :appointments, :doctors, :users
# The following passes:
def setup
login_as :admin
end
test "should show doctor appointment" do
get :show, :id => doctors(:one).to_param, :appointment_id => appointments(:app_one).id
assert_response :success
end
end
...and now I get this error:
1) Error:
test_should_show_doctor_appointment(Admin::AppointmentsControllerTest):
RuntimeError: #controller is nil: make sure you set it in your test's setup method.
test/functional/admin/appointments_controller_test.rb:13:in `test_should_show_doctor_appointment'
At this point, I added #controller = AppointmentsController.new under the setup method, only to get the very familiar:
1) Error:
test_should_show_doctor_appointments(Admin::AppointmentsControllerTest):
ActionController::RoutingError: No route matches {:action=>"show", :controller=>"appointments", :doctor_id=>2, :id=>"281110143"}
test/functional/admin/appointments_controller_test.rb:14:in `test_should_show_doctor_appointments'
It seems like a vicious circle to me.
Thanks anyways...
pR
You should probably be doing this instead:
#controller = Admin::AppointmentsController.new
Otherwise you're referencing the controller inside the main namespace and not the Admin namespace.

No route matches although there is one in rake routes

I get strange ActionController::RoutingError: No route matches. The route can be generated:
> r = Rails.application.routes
> r.generate controller: :items, action: :index, user_id:1
=> ["/users/1/items", {}]
And it is in the rake routes:
$ rake routes
user_items GET /users/:user_id/items(.:format) items#index
POST /users/:user_id/items(.:format) items#create
new_user_item GET /users/:user_id/items/new(.:format) items#new
edit_user_item GET /users/:user_id/items/:id/edit(.:format) items#edit
user_item GET /users/:user_id/items/:id(.:format) items#show
PUT /users/:user_id/items/:id(.:format) items#update
DELETE /users/:user_id/items/:id(.:format) items#destroy
(...)
But I it's not recognized:
r.recognize_path "/users/1/items"
ActionController::RoutingError: No route matches "/users/1/items"
My routes.rb:
Sumo2::Application.routes.draw do
resources :users do
resources :items
end
(...)
end
Any ideas?
Do you have ItemsController class defined? Without it your route won't be recognized. Perhaps you misspelled it.
irb(main):001:0> r = Rails.application.routes
=> #<ActionDispatch::Routing::RouteSet:0x1cbba00>
irb(main):002:0> r.recognize_path "/users/1/items"
ActionController::RoutingError: No route matches "/users/1/items"
... errors ...
irb(main):003:0> class ItemsController; end # <----------<
=> nil
irb(main):004:0> r.recognize_path "/users/1/items"
=> {:action=>"index", :controller=>"items", :user_id=>"1"}

Testing nested resources with RSpec

I am trying to create tests for nested resources in Rails. The relevant route definition is:
resources :communities do
resources :contents, :type => 'Content'
end
Using RSpec and factory_girl, I am trying to get started with testing with e.g.
describe ContentsController do
it 'should display a content item under a community' do
content = FactoryGirl.create(:content)
get :show, :community_id => content.community.id, :id => content.id
end
end
These requests always result in
Failure/Error: get :show, :community_id => content.community.id, :id => content.id
ActionController::RoutingError:
No route matches {:community_id=>BSON::ObjectId('4e7773c6ac54c3d1ad000002'),
:id=>BSON::ObjectId('4e7773c6ac54c3d1ad000001'), :controller=>"contents",
:action=>"show"}
For the life of me I cannot find a way to specify a route to a nested resource with RSpec. Am I doing something fundamentally wrong here?
Update: The relevant part of rake routes is:
community_contents GET /communities/:community_id/contents(.:format) {:action=>"index", :controller=>"contents"}
POST /communities/:community_id/contents(.:format) {:action=>"create", :controller=>"contents"}
new_community_content GET /communities/:community_id/contents/new(.:format) {:action=>"new", :controller=>"contents"}
edit_community_content GET /communities/:community_id/contents/:id/edit(.:format) {:action=>"edit", :controller=>"contents"}
community_content GET /communities/:community_id/contents/:id(.:format) {:action=>"show", :controller=>"contents"}
PUT /communities/:community_id/contents/:id(.:format) {:action=>"update", :controller=>"contents"}
DELETE /communities/:community_id/contents/:id(.:format) {:action=>"destroy", :controller=>"contents"}
I see that you are passing the content.community.id as the :community_id and that object looks like a mongo document that is identified with a BSON::ObjectId. Try to use to_param instead as following:
get :show, :community_id => content.community.to_param, :id => content.to_param

Resources