Rails 3 Tutorial Section 7.3.1 Routing Error - ruby-on-rails

I get the following error when running rspec spec/controllers/users_controller_spec.rb in section 7.3.1 of Michael Hartl's Rails Tutorial:
Failure/Error: get :show, :id => #user
ActionController::RoutingError:
No route matches {:id=>#<User id: 1, #rest is data from the factories.rb file....
Here's my code for the users_controller_spec.rb file:
require 'spec_helper'
require 'factories'
describe UsersController do
render_views
describe "GET 'show'" do
before(:each) do
#user = Factory(:user)
end
it "should be successful" do
get :show, :id => #user
response.should be_success
end
it "should find the right user" do
get :show, :id => #user
assigns(:user).should == #user
end
end
describe "GET 'new'" do
it "should be successful" do
get 'new'
response.should be_success
end
it "should have the right title" do
get 'new'
response.should have_selector("title", :content => "Sign up")
end
end
end
Here is my factories.rb code:
Factory.define :user do |user|
user.name "Michael Hartl"
user.email "mhartl#example.com"
user.password "foobar"
user.password_confirmation "foobar"
end
I inserted these lines in Spec_Helper in regards to 'factory_girl':
require 'factory_girl'
Factory.find_definitions
Any idea what's causing the routing error?
Here is my routes.rb code:
SampleApp::Application.routes.draw do
get "users/new"
match '/signup', :to => 'users#new'
match '/contact', :to => 'pages#contact'
match '/about', :to => 'pages#about'
match '/help', :to => 'pages#help'
root :to => 'pages#home'
The author's note regarding using - get :show, :id => #user instead of using - get :show, :id => #user.id:
" Second, note that the value of the hash key :id, instead of being the user’s id attribute #user.id, is the user object itself:
get :show, :id => #user
We could use the code:
get :show, :id => #user.id
to accomplish the same thing, but in this context Rails automatically converts the user object to the corresponding id. It does this by calling the to_param method on the #user variable.

You are missing a route to the show action for users. You can add something like this to the routes.rb file.
match "/users/:id" => "users#show"
If you want the normal CRUD actions, you can get rid of your users/new route and instead of the match line above, just declare them all with a single line:
resources :users

I believe your code is telling Rails that the id is the user, which of course doesn't make sense. That's creating a path like /users/#user, for which no route exists. As you probably know, you want a path like /users/1.
So I think you code should look like
get :show, :id => #user.id
or possibly
get :show, #user

Related

Rails Spec Controller Test failing with custom route

Rails 5.1
RSpec 3.6
I have a Controller:
class SessionController < ApplicationController
def new
end
end
A custom route:
get 'login' => 'sessions#new'
RSpec Test:
require 'rails_helper'
RSpec.describe SessionController, type: :controller do
describe "GET #new" do
before do
routes.draw { get "login" => "sessions#new" }
end
it "returns http success" do
get :login
expect(response).to have_http_status(:success)
end
end
end
and get error:
ActionController::UrlGenerationError: No route matches {:action=>"login", :controller=>"session"}
So "get" within a controller test seems always map to the action not the route. What should i do to get this test run? thanks in advance.
ActionController::UrlGenerationError: No route matches
{:action=>"login", :controller=>"session"}
Your controller name is SessionController, so your route should be
get 'login' => 'session#new' not get 'login' => 'sessions#new'
require 'rails_helper'
RSpec.describe SessionController, type: :controller do
describe "GET #new" do
before do
routes.draw { get "login" => "session#new" }
end
it "returns http success" do
get :login
expect(response).to have_http_status(:success)
end
end
end
Change it in your routes.rb as well.
When you are writing tests and you use the methods get, post, delete, etc., those methods assume that any parameter you pass them is the name of an action within the controller being tested. So, this works:
get :new
because it generates url_for(:controller => :sessions, :action => :new).
This doesn't work:
get '/login'
because it generates url_for(:controller => :sessions, :action => '/login').

Rspec no route matches, checking for flash message in controller

I need to test that a flash message comes up when a specific exception is raised. I am getting a no route matches error and have looked through the other topics with "Rspec Controller test, No Routes matches" and am still not sure what is wrong. This is my first time writing an rspec test. Commented out lines are various other attempts to get test to pass.
audit/businesses_controller.rb:
class Audit::BusinessesController < ApplicationController
# PUT /audit/businesses/:id
def update
remove_deleted_account_numbers_in(params)
#business = #jurisdiction.businesses.find(params[:id])
#business.changed_by = current_user
if #business.update_attributes(audit_business_params)
success_message('update')
else
#business.initialize_accounts
#business = #business
render :edit, alert: t('flash.save_failed')
end
rescue ActiveRecord::StatementInvalid => e
if e.message.include? "foreign key constraint"
#business = #jurisdiction.businesses.find(params[:id])
#business.initialize_accounts
#business = #business
render :edit, alert: "This account number is in use and cannot be deleted"
else
raise e
end
end
business_controller_spec.rb:
require "spec_helper"
describe Audit::BusinessesController do
describe "handling ActiveRecord::StatementInvalid" do
before {delete, url = 'audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format)'}
it "should rescue with a flash message" do
# /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format)
# delete, businesses_id: '80776', jurisdiction_id: '795', format: 'html', id: "234769"
expect(page).to have_content("This account number is in use and cannot be deleted")
end
end
end
rake routes:
audit_businesses GET
/audit/jurisdictions/:jurisdiction_id/businesses(.:format)
audit/businesses#
POST /audit/jurisdictions/:jurisdiction_id/businesses(.:format)
audit/businesses#create
new_audit_business GET
/audit/jurisdictions/:jurisdiction_id/businesses/new(.:format)
audit/businesses#new
edit_audit_business GET
/audit/jurisdictions/:jurisdiction_id/businesses/:id/edit(.:format)
audit/businesses#edit
audit_business
GET /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format)
audit/businesses#show
PUT /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format) audit/businesses#update
DELETE /audit/jurisdictions/:jurisdiction_id/businesses/:id(.:format) audit/businesses#destroy
Error Message:
Failure/Error: before { delete audit_business_path(:jurisdiction_id, :id, :format) }
ActionController::RoutingError:
No route matches {:controller=>"audit/businesses", :action=>"/audit/jurisdictions/jurisdiction_id/businesses/id.format"}
# ./spec/controllers/businesses_controller_spec.rb:6:in `block (3 levels) in <top (required)>'
When I change the spec to:
describe Audit::BusinessesController do
describe "handling ActiveRecord::StatementInvalid" do
it "should rescue with a flash message" do
put :update, :jurisdiction_id => 1, :id => 1, :format => html
expect(controller).to set_flash[:alert].to(/This account number is in use and cannot be deleted/).now
The error is:
Failure/Error: put :update, :jurisdiction_id => 1, :id => 1, :format => html
NoMethodError:
undefined method `can_update?' for nil:NilClass
routes.rb:
namespace :audit do
root to: 'site#home'
resources :labels, only: ['index', 'destroy'] do
post 'delete_queue', on: :collection
get 'print', on: :collection
get 'print_view', on: :collection
get 'mark_printed', on: :collection
get 'users', on: :collection
end
resources :auditor_goals
resources :contracts
resource :contract_work, only: %w[show update], controller: 'contract_work'
resources :manpower
resource :county_budgets, only: :update do
get 'search'
end
get 'intro_letter_search' => 'files#intro_letter_search'
put 'intro_letter_update' => 'files#intro_letter_update'
get 'search' => 'files#search'
get 'calendar' => 'files#calendar'
get 'autocomplete' => 'files#autocomplete'
get 'direct' => 'files#direct'
get 'size_code_select' => 'businesses#size_code_select'
scope '/jurisdictions/:jurisdiction_id' do
resources :businesses, controller: 'businesses'
resources :files, controller: 'files' do
get 'print', on: :member
end
# get 'page/:page', action: :index, on: :collection
end
end
for rspec test instead write
expect(page).to have_content("flash message")
where "flash message" is the actual message of the error or exception you're trying to make pass. Eg for, flash[:notice] = "An error occurred", you would put have_content("An error occurred")
Hope that helps?!
Justin
#TzokinB - I believe "Capybara::ElementNotFound: Unable to find xpath "/html" typically means the test set is now looking for a view. Have you associated a view for to action in your controller?

No Route Matches ... Rails Engine

So I keep getting the error:
No route matches {:action=>"create", :controller=>"xaaron/api_keys"}
Which is thrown in the test:
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
when I go to spec/dummy and run the rake routes command I see:
api_keys GET /api_keys(.:format) xaaron/api_keys#index
POST /api_keys(.:format) xaaron/api_keys#create
new_api_key GET /api_keys/new(.:format) xaaron/api_keys#new
edit_api_key GET /api_keys/:id/edit(.:format) xaaron/api_keys#edit
api_key GET /api_keys/:id(.:format) xaaron/api_keys#show
PATCH /api_keys/:id(.:format) xaaron/api_keys#update
PUT /api_keys/:id(.:format) xaaron/api_keys#update
DELETE /api_keys/:id(.:format) xaaron/api_keys#destroy
Which shows that yes this route does exist. My routes file for this engine looks like:
Xaaron::Engine.routes.draw do
get 'login' => 'sessions#new', :as => 'login'
get 'logout' => 'sessions#destroy', :as => 'logout'
get 'signup' => 'users#new', :as => 'signup'
get 'permission_denied' => 'error#denied', :as => 'permission_denied'
get 'record_not_found' => 'error#error', :as => 'record_not_found'
get 'password_reset' => 'password_resets#edit', :as => 'rest_user_password'
resource :error, controller: 'error'
resources :users
resources :api_keys
resources :sessions
resources :roles
resources :password_resets
end
What am I missing?
update
For those of you curious how I am getting these routes, its because the dummy app's routes file is set up (by default) as such:
Rails.application.routes.draw do
mount Xaaron::Engine => "/xaaron"
end
Update II
I have been reading this api docs on how routing is done in engines and I believe the way I have done this is correct, how ever the controller is defined as such:
module Xaaron
class ApiKeysController < ActionController::Base
before_action :authenticate_user!
def index
#api_key = Xaaron::ApiKey.where(:user_id => current_user.id)
end
def create
#api_key = Xaaron::ApiKey.new(:user_id => current_user.id, :api_key => SecureRandom.hex(16))
create_api_key(#api_key)
end
def destroy
Xaaron::ApiKey.find(params[:id]).destroy
flash[:notice] = 'Api Key has been deleted.'
redirect_to xarron.api_keys_path
end
end
end
You need to tell your spec you are using the engine routes:
describe ApiKeysController do
routes { Xaaron::Engine.routes }
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
end

Ruby on Rails - Rspec - Controller test - nested route

I got a pretty basic controller test
require 'spec_helper'
describe Admin::OrdersController do
describe "GET #order_detail" do
before :each do
new_admin = FactoryGirl.create(:admin)
sign_in new_admin
#storefront = FactoryGirl.create(:storefront)
#order = FactoryGirl.create(:order)
end
it "assigns the requested order to #order" do
get :order_detail, { :storefront_id => #storefront.id, :order_id => #order.id }
assigns(:order).should eq(#order)
end
it "renders the :show template" do
get :order_detail, {:storefront_id => #storefront.id, :order_id => #order.id}
response.should render_template :order_detail
end
end
end
Which gets me the following error for both actions:
ActionController::RoutingError:
No route matches {:storefront_id=>"14", :order_id=>"1", :controller=>"admin/orders", :action=>"order_detail"}
From the routes.rb:
resources :storefronts do
resources :orders do
member do
get :order_detail
end
end
end
I thought
get :order_detail, { :storefront_id => #storefront.id, :order_id => #order.id }
would be the right way to generate the route but unfortunately it's not.
You can see the routes generated by Rails using rake routes or bundle exec rake routes in the application root. I did the same resource set-up in a brand new rails app, and rake routes output is as follows (only for order detail route):
order_detail_storefront_order GET /storefronts/:storefront_id/orders/:id/order_detail(.:format) orders#order_detail
As you can see, rails is expecting :id and not :order_id. Try changing :order_id to :id as the parameter key in your spec.

Testing an RSpec controller action that can't be accessed directly

I've got a controller that can't be accessed directly, in the traditional RESTful way, but rather only through a particular url.
Normally I'm used to using get and post in my controller specs to call controller actions. Is there a way that I can exercise my controller by visiting a particular url?
EDIT:
Here is my route:
Larzworld::Application.routes.draw do
match '/auth/:provider/callback' => 'authentications#create'
devise_for :users, :controllers => {:registrations => "registrations"}
root :to => 'pages#home'
end
Here is my spec:
require 'spec_helper'
describe AuthenticationsController do
before(:each) do
request.env["omniauth.auth"] = {"provider" => "twitter", "uid" => "12345678"}
end
describe 'POST create' do
it "should find the Authentication using the uid and provider from omniauth" do
Authentication.should_receive(:find_by_provider_and_uid)
post 'auth/twitter/callback'
end
end
end
and here is the error I receive:
Failures:
1) AuthenticationsController POST create should find the Authentication using the uid and provider from omniauth
Failure/Error: post 'auth/twitter/callback'
No route matches {:action=>"auth/twitter/callback", :controller=>"authentications"}
# ./spec/controllers/authentications_controller_spec.rb:13
Finished in 0.04878 seconds
1 example, 1 failure
Controller tests use the four HTTP verbs (GET, POST, PUT, DELETE), regardless of whether your controller is RESTful. So if you have a non-RESTful route (Rails3):
match 'example' => 'story#example'
the these two tests:
require 'spec_helper'
describe StoryController do
describe "GET 'example'" do
it "should be successful" do
get :example
response.should be_success
end
end
describe "POST 'example'" do
it "should be successful" do
post :example
response.should be_success
end
end
end
will both pass, since the route accepts any verb.
EDIT
I think you're mixing up controller tests and route tests. In the controller test you want to check that the logic for the action works correctly. In the route test you check that the URL goes to the right controller/action, and that the params hash is generated correctly.
So to test your controller action, simply do:
post :create, :provider => "twitter"`
To test the route, use params_from (for Rspec 1) or route_to (for Rspec 2):
describe "routing" do
it "routes /auth/:provider/callback" do
{ :post => "/auth/twitter/callback" }.should route_to(
:controller => "authentications",
:action => "create",
:provider => "twitter")
end
end

Resources