Route does not match in test but is recognized in application - ruby-on-rails

I am following Michael Hartl's tutorial, chapter 10. Test UsersControllerTest#test_should_redirect_edit_when_logged_in_as_wrong_user fails when trying to do get edit_user_path(#user).
get edit_user_path(#user)
ActionController::UrlGenerationError: No route matches {:action=>"/users/762146111/edit", :controller=>"users"}
from /Users/cello/.rbenv/versions/2.3.3/lib/ruby/gems/2.3.0/gems/actionpack-5.1.4/lib/action_dispatch/journey/formatter.rb:55:in `generate'
However:
Rails.application.routes.recognize_path '/users/762146111/edit', method: :get
=> {:controller=>"users", :action=>"edit", :id=>"762146111"}
Below is the code that might have a bug (Rails 5.1.4).
routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
post 'signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
get 'sessions/new'
resources :users
end
users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
#user = users(:michael)
#other_user = users(:archer)
end
test 'should redirect edit when logged in as wrong user' do
log_in_as(#other_user)
get edit_user_path(#user)
assert flash.empty?
assert_redirected_to root_url
end
end
users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
def edit
#user = User.find(params[:id])
end
private
def logged_in_user
unless logged_in?
flash[:danger] = 'Please log in.'
redirect_to login_url
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
end

The tutorial is defining an Integration Test (inherits from ActionDispatch::IntegrationTest), whereas your above code is defining a Controller Test (inherits from ActionController::TestCase).
get :edit, ... is the correct syntax for a controller test, because it bypasses URL recognition and directly specifies the :action. This is confusing, and is one of several reasons that controller tests are now discouraged in favour of integration tests, which is probably what you want to create.
To do so, change:
class UsersControllerTest < ActionController::TestCase
to:
class UsersControllerTest < ActionDispatch::IntegrationTest
(Note the tutorial, somewhat confusingly, uses ActionDispatch::IntegrationTest as the base class both for tests it puts in tests/integration/ and those it puts in tests/controllers/.)

You can not use direct url with 'get' method in spec.
In your spec instead of
get edit_user_path(#user)
use
get :edit, params: { id: #user.id }
the same with
patch user_path(#user)
use patch :update instead

Related

Admin function in rails: Error: Couldn't find User with 'id'=

I am trying to write an update function, which allows to pick a user in a list of users and update that user to make it an admin. The important function in the controller should be def change_admin.
Thanks for your help!
I tried several options, but I run into that error:
Couldn't find User with 'id'=
My Controller:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def new
#user = User.new
end
def index
#users = User.where(activated: true).paginate(page: params[:page])
end
def show
#user = User.find(params[:id])
redirect_to root_url and return unless #user.activated
end
def edit
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "Der Nutzer wurde gelöscht"
redirect_to users_url
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
redirect_to root_path
else
render 'edit'
end
end
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
flash[:info] = "Bitte öffnen Sie Ihr E-Mail Postfach, um den Account zu aktivieren."
redirect_to root_url
else
render 'new'
end
end
def admin
#users = User.where(activated: true).paginate(page: params[:page])
end
def change_admin
#user = User.find(params[:id])
#user.update_attribute(:admin,true)
respond_to do |format|
format.html { redirect_to admin_path }
end
end
# Before filters
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user)
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation,
:mat_number, :ects, :grade_avg, :enrolled, :matched_institute)
end
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
I also tried to delete the user.find line, but that gives me another error:
undefined method `update_attribute' for nil:NilClass
My routes file:
Rails.application.routes.draw do
resources :preferences
resources :institutes
get 'password_resets/new'
get 'password_resets/edit'
get '/users_show', to: 'users#show'
get '/users/new'
root 'static_pages#home'
get '/home', to: 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/matching', to: 'static_pages#matching'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/admin', to: 'users#admin'
post '/change_admin', :to => 'users#change_admin', as: 'change_admin'
get '/performance_show', to: 'users#performance_show'
get '/performance_update', to: 'users#performance_update'
post 'preferences/create_all', :to => 'preferences#create_all'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
post 'preferences/delete_matching', :to => 'preferences#delete_matching'
post 'preferences/read_and_show_ofv', :to => 'preferences#read_and_show_ofv'
post 'preferences/read_matching', :to => 'preferences#read_matching'
post 'preferences/optimize_matching', :to => 'preferences#optimize_matching'
post 'preferences/optimize', to: 'preferences#optimize'
end
Your routes are in pretty bad shape. You have tons of duplication as well as routes that are missing the :id segment to make them work:
get '/users_show', to: 'users#show'
get '/users/new'
post '/change_admin', :to => 'users#change_admin', as: 'change_admin'
resources :users already declares a proper show route as GET /users/:id and new as /users/new.
To add additional RESTful routes you should instead pass a block to resources:
resources :users do
# this should be PATCH not POST
patch :change_admin
end
This will create the proper route as /users/:id/change_admin.
You are also using the wrong HTTP verb in many places like for example get '/performance_update', to: 'users#performance_update'. Never use GET for actions that create or alter a resource as the call ends up in the browsers history and may be cached.
Adding update, change, create in the path of your route should be a big red flag that you´re doing it wrong.
I would recommend that you read the guides thoroughly before adding more cruft.

2 tests fail at chapter 10.2.1 end: No route matches

I'm having problems while trying to pass the test at the end of section 10.2.1 of Michael Hartl's Rails Tutorial book at the current state of the art. In particular, when I uncomment the before_action line in app/controllers/users_controller.rb according to listing 10.21 and then run the test suite as to the subsequent listing 10.22 (where the tests should be green), two tests are failing, namely test_should_redirect_edit_when_not_logged_in and test_should_redirect_update_when_not_logged_in. It looks very strange to me since I succeeded all the steps until this point :\
Anyway, here's the error code:
ERROR["test_should_redirect_edit_when_not_logged_in", UsersControllerTest, 1.0972138489887584]
test_should_redirect_edit_when_not_logged_in#UsersControllerTest (1.10s)
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"users", :id=>nil} missing required keys: [:id]
test/controllers/users_controller_test.rb:10:in `block in <class:UsersControllerTest>'
ERROR["test_should_redirect_update_when_not_logged_in", UsersControllerTest, 1.1339553739962867]
test_should_redirect_update_when_not_logged_in#UsersControllerTest (1.13s)
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"users", :id=>nil} missing required keys: [:id]
test/controllers/users_controller_test.rb:16:in `block in <class:UsersControllerTest>'
But according to rake routes the routes above are already there!
Prefix Verb URI Pattern Controller#Action
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
Here's my app/controllers/users_controller.rb:
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Sample App!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
my test/controllers/users_controller_test.rb:
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get signup_path
assert_response :success
end
test "should redirect edit when not logged in" do
get edit_user_path(#user)
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch user_path(#user), params: { user: { name: #user.name,
email: #user.email } }
assert_not flash.empty?
assert_redirected_to login_url
end
end
and my config/routes.rb:
Rails.application.routes.draw do
get 'sessions/new'
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
post '/signup', to: 'users#create'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
end
Hope some bold heroes will help! :) Thanks a lot in advance
test "should redirect edit when not logged in" do
get edit_user_path(#user)
#user is nil. This is what causes the error. I guess you skipped over a small snippet of code.

No route matches {:action=>"/microposts", :controller=>"microposts", :params=>{:micropost=>{:content=>"Lorem ipsum"}}}

I am currently stuck on the rails tutorial from Michael Hartl (railstutorial.org), Chapter 13
and getting the following two errors:
1) Error:
MicropostsControllerTest#test_should_redirect_create_when_not_logged_in:
ActionController::UrlGenerationError: No route matches {:action=>"/microposts", :controller=>"microposts", :params=>{:micropost=>{:content=>"Lorem ipsum"}}}
test/controllers/microposts_controller_test.rb:11:in 'block (2 levels) in <class:MicropostsControllerTest>'
test/controllers/microposts_controller_test.rb:10:in 'block in <class:MicropostsControllerTest>'
2) Error:
MicropostsControllerTest#test_should_redirect_destroy_when_not_logged_in:
ActionController::UrlGenerationError: No route matches {:action=>"/microposts/499495288", :controller=>"microposts"}
test/controllers/microposts_controller_test.rb:18:in 'block (2 levels) in <class:MicropostsControllerTest>'
test/controllers/microposts_controller_test.rb:17:in 'block in <class:MicropostsControllerTest>'
As far as i know, 'action' should be something like get, post, delete etc.
But i don't know, why it says 'micropost' here.
Content of microposts_controller_test.rb:
require 'test_helper'
class MicropostsControllerTest < ActionController::TestCase
def setup
#micropost = microposts(:orange)
end
test 'should redirect create when not logged in' do
assert_no_difference 'Micropost.count' do
post microposts_path, params: {micropost: {content: 'Lorem ipsum'}}
end
assert_redirected_to login_url
end
test 'should redirect destroy when not logged in' do
assert_no_difference 'Micropost.count' do
delete micropost_path(#micropost)
end
assert_redirected_to login_url
end
end
Content of micropost_controller.rb:
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = 'Micropost created!'
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
private
def micropost_params
params.require(:micropost).permit(:content)
end
end
Content of routes.rb:
Rails.application.routes.draw do
root 'static_pages#home'
get '/help' => 'static_pages#help'
get '/about' => 'static_pages#about'
get '/contact' => 'static_pages#contact'
get '/signup' => 'users#new'
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
delete '/logout' => 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
end
Any help is appreciated.
Thanks
Edit:
In micropost_controller_test.rb, it should've been:
class MicropostsControllerTest < ActionDispatch::IntegrationTest
instead of
class MicropostControllerTest < ActionCntroller::TestCase
The problem with the first test is that you dont have a route in the controller /microposts" on which the test is trying to hit and if you see your routes file it confirms resources :microposts, only: [:create, :destroy], so no route for that. Secondly after destroy it should redirect and in your controller method no redirection is there. So eventually test fails.
In micropost_controller_test.rb, it should've been:
class MicropostsControllerTest < ActionDispatch::IntegrationTest
instead of
class MicropostControllerTest < ActionCntroller::TestCase

No route error when GETting root_path, but only in some of my tests

I have a root_path defined in routes.rb and I call get root_path in other tests, but for some reason, in test/controllers/application_controller_test.rb, I get this error when calling get root_path:
ApplicationControllerTest#test_should_get_index_when_not_logged_in:
ActionController::UrlGenerationError: No route matches {:action=>"/", :controller=>"application"}
test/controllers/application_controller_test.rb:10:in `block in <class:ApplicationControllerTest>'
Here's routes.rb
Rails.application.routes.draw do
root 'application#index'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:edit, :new, :create, :update]
resources :lessons, only: [:show, :index] do
resources :pre_lesson_surveys, shallow: true,
except: :destroy
end
end
Here's application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
def index
render 'admin_home_page' if admin?
render 'user_home_page' if logged_in?
#user = User.new unless logged_in?
end
end
and here's one of the tests
test "should get index when not logged in" do
get root_path
assert_response :success
assert_not is_logged_in?
assert_template 'application/index'
end
I'm sure I'm just doing something stupid, but I can't put my finger on it
If you want to make sure that the index template gets rendered on a request to your root path, a request spec may be appropriate:
spec/requests/application_requests_spec.rb:
describe "Test Root Path" do
it 'successfully renders the index template on GET /' do
get "/"
expect(response).to be_successful
expect(response).to render_template(:index)
end
end
If you want to make sure that the index template gets rendered on a request to the index action of your ApplicationController, a controller spec may be appropriate.
spec/controllers/application_controller_spec.rb:
describe ApplicationController do
describe "GET index" do
it "successfully renders the index template" do
expect(controller).to receive(:index)
get :index
expect(response).to be_successful
expect(response).to render_template(:index)
end
end
end

Rails Routing Error? 'No route matches'

So I keep running into the following error:
No route matches {:action=>"show", :controller=>"users"}
I tried running rake routes and I can see that the route exists:
user GET /users/:id(.:format) users#show
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
But every time I try visiting the page "/users/1" (1 being the user id), I get the above error. Any ideas? Thanks!
Here's my routes.rb:
SampleApp::Application.routes.draw do
root to: 'static_pages#home'
resources :users
resource :sessions, only: [:new, :create, :destroy]
match '/signup', to: 'users#new'
match '/signin', to: 'sessions#new'
match '/signout', to: 'sessions#destroy', via: :delete
match '/help', to: 'static_pages#help'
match '/about', to: 'static_pages#about'
match '/contact', to: 'static_pages#contact'
Here's my users_controller.rb:
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update]
before_filter :correct_user, only: [:edit, :update]
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Paper Piazza!"
redirect_to #user
else
render 'new'
end
end
def edit
#user = User.find(params[:id])
end
def update
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
def index
#users = User.paginate(page: params[:page])
end
private
def signed_in_user
unless signed_in?
store_location
redirect_to signin_path, notice: "Please sign in."
end
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user)
end
end
Try fixing the typo:
root :to 'static_pages#home'
(rather than root to:), and moving it to the last line of the block. And let me know if that makes a difference!
What's odd is that I built a fresh project with a routing file that simply reads:
RoutingTest::Application.routes.draw do
resources :users
root :to => "static_pages#home"
end
When I ran this in the console, I got the same error you're seeing:
>> r = Rails.application.routes ; true
=> true
>> r.recognize_path("/users/1")
=> ActionController::RoutingError: No route matches "/users"
... but when I run the same thing in a slightly older project, I get:
>> r = Rails.application.routes ; true
=> true
>> r.recognize_path("/users/1")
=> {:action=>"show", :controller=>"users", :id=>"1"}
So I have little confidence that what I'm telling you will make a difference. (Aside from that, the Rails.application.routes trick is useful for verifying paths in the console!)
I had a similar problem, I had routes that would just return the root page, but would show up in rake routes. According to a fellow developer for a specific namespace and for all routes the root to: (routing) must always be last. This is in contradiction to the Rails guide on routing (see http://guides.rubyonrails.org/routing.html#using-root) which says that any call to root should be at the top of your file. That was definitely not working for me. Interesting...

Resources