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.
Related
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
I'm getting two different test errors in Chapter 8.2.1 of Michael Hartl's Ruby on Rails Tutorial. I'm new to Rails, but I have triple checked for syntax errors and anything else I could find. Any help is very much appreciated.
Error Message 1:
ERROR["test_should_get_new", Minitest::Result, 0.9693643249993329]
test_should_get_new#Minitest::Result (0.97s)
NameError: NameError: undefined local variable or method users_new_url' for #<UsersControllerTest:0x00000006e953f8>
test/controllers/users_controller_test.rb:5:inblock in '
Error Message 2:
ERROR["test_invalid_signup_information", Minitest::Result, 0.8977870759990765]
test_invalid_signup_information#Minitest::Result (0.90s)
ActionController::RoutingError: ActionController::RoutingError: No route matches [POST] "/signup"
test/integration/users_signup_test.rb:8:in block (2 levels) in <class:UsersSignupTest>'
test/integration/users_signup_test.rb:7:inblock in '
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'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
end
Sessions Controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
# Log the user in and redirect to the user's show page.
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
end
end
Users Controller:
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
flash[:success] = "Welcome to your new profile!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
Users controller test
require 'test_helper'
class UsersControllerTest < ActionDispatch::IntegrationTest
test "should get new" do
get users_new_url
assert_response :success
end
end
Users Signup Test
require 'test_helper'
class UsersSignupTest < ActionDispatch::IntegrationTest
test "invalid signup information" do
get signup_path
assert_no_difference 'User.count' do
post users_path, params: { user: { name: "",
email: "user#invalid",
password: "foo",
password_confirmation: "bar" } }
end
assert_template 'users/new'
assert_select 'div#error_explanation'
assert_select 'div.field_with_errors'
assert_select 'form[action="/signup"]'
end
test "valid signup information" do
get signup_path
assert_difference 'User.count', 1 do
post users_path, params: { user: { name: "Example User",
email: "user#example.com",
password: "password",
password_confirmation: "password" } }
end
follow_redirect!
assert_template 'users/show'
assert_not flash.nil?
end
end
In your users_controller_test, the code doesn't know what users_new_url is. This is probably because that route doesn't exist. You will most likely have to do something like get new_user_path, but you can find out by typing rake routes and getting the list of your available routes.
Here's an example of what rake routes will output:
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
You can reference a named path by appending _path to the name. i.e. users_path will map to the "users#index"controller and method.
rake routes will also help for your second problem, which is that you do not have a route defined for POST /signup -- you have a GET /signup.
So, you'll need to add a line like:
post '/signup', to: 'users#create'
This route will map to your UsersController's #create method, which I see you have in your code.
I've been following the Michael Hartl tutorial for learning rails and have been doing pretty well thus far, but I'm stumped on this issue which I've encountered repeatedly. I'm not skilled enough to know if there is a configuration problem in my environment or something else, but this makes NO sense to me.
In ANY of the controller tests I attempt to run, I can never get my helper URL methods to work. As an example:
test "should redirect home when not an admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert redirect_to root_url
end
Generates the following error:
ERROR["test_should_redirect_home_when_not_an_admin", UsersControllerTest, 0.9899668790167198]
test_should_redirect_home_when_not_an_admin#UsersControllerTest (0.99s)
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"/users/762146111", :controller=>"users"}
test/controllers/users_controller_test.rb:60:in `block (2 levels) in <class:UsersControllerTest>'
test/controllers/users_controller_test.rb:59:in `block in <class:UsersControllerTest>'
Even simple tests like
test "should redirect index when not logged in" do
get users_path
assert_redirected_to login_url
end
Produce the same error:
ERROR["test_should_redirect_index_when_not_logged_in", UsersControllerTest, 1.5291320629185066]
test_should_redirect_index_when_not_logged_in#UsersControllerTest (1.53s)
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"/users", :controller=>"users"}
test/controllers/users_controller_test.rb:34:in `block in <class:UsersControllerTest>'
Everything I've googled about this issue hasn't helped, because for some reason I cannot determine the user_path method (or any other similar method) somehow thinks the action I'm trying to take in my controller is the path!
I've verified that my routes file is configured correctly.
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'
post '/signup' => 'users#create'
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
delete '/logout' => 'sessions#destroy'
get 'sessions/new'
resources :users
end
I've checked that running "rails routes" has the correct routes. I've even checked in rails console that "app.user_path(1)" spits out a valid path.
At this point I'm just beyond stumped as to why these helper methods don't seem to be helping... I also don't know what they're actually called so my googling has been fairly fruitless.
To get around this issue in the tutorial, I've been using syntax like
patch :edit, { id: #user }, params: { user: { name: #user.name,
email: #user.email }}
Or
test "should redirect index when not logged in" do
get :index
assert_redirected_to login_url
end
Which seems to work.
Also here is one of my test files if that's helpful:
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
#user = users(:michael)
#other_user = users(:archer)
end
test "should get new" do
get :new
assert_response :success
end
test "should redirect edit when user not logged in" do
get :edit, params: { id: #user }
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when user not logged in" do
patch :edit, { id: #user }, params: { user: { name: #user.name,
email: #user.email }}
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect index when not logged in" do
# get users_path
get :index
assert_redirected_to login_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert redirect_to login_url
end
test "should redirect home when not an admin" do
log_in_as(#other_user)
assert_no_difference 'User.count' do
delete user_path(#user)
end
assert redirect_to root_url
end
end
Please let me know what other files I can post to be helpful.
I had the same issue and I fixed it by changing the file 'users_controller_test.rb' file replacing:
class UsersControllerTest < ActionController::TestCase
for
class UsersControllerTest < ActionDispatch::IntegrationTest
But first, in your case, check that you have in your Gemfile the line:
gem 'rails', '5.0.1'
as the tutorial shows.
I can also see that you have the following test:
test 'should get new' do
get :new
assert_response :success
end
When you make the correction you'll have to change the above test to:
test 'should get new' do
get new_user_path
assert_response :success
end
After that, run your tests and you should have them all passed.
Hope it helps
Update - typical user error - guys thanks for all the technical help! It looks like the password_reset_controller_test should have never been made. For some reason - my control exists even though there was a "--no-test-framework" used in 12.1.1. I'm pretty sure I mistyped something & that's why the tutorial doesn't have a ID for the user on it. To recap: the solution was removing the controller test - as there's an integration test made later.
I've got an issue with an error claiming no route, but can clearly see the route in the routes.rb file & in the rake routes. I'm using Michael Hartl's Ruby on Rails tutorial guide. In chapter 12, I've got a failing test. I tested this by removing and adding back several parts - it appears to actually be in the controller, even though it claims no action found. I also checked for params issues as best as I know how with my limited knowledge.
Any hints would be appreciated!
Thanks ...
ERROR["test_should_get_edit", PasswordResetsControllerTest, 2016-10-20 15:24:37 +0000]
test_should_get_edit#PasswordResetsControllerTest (1476977077.08s)
ActionController::UrlGenerationError: ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"password_resets"}
test/controllers/password_resets_controller_test.rb:11:in `block in <class:PasswordResetsControllerTest>'
test/controllers/password_resets_controller_test.rb:11:in `block in <class:PasswordResetsControllerTest>'
54/54: [=======================================================================] 100% Time: 00:00:03, Time: 00:00:03
Finished in 3.02339s
54 tests, 279 assertions, 0 failures, 1 errors, 0 skips
Controller Test
password_resets_controller_test
require 'test_helper'
class PasswordResetsControllerTest < ActionController::TestCase
test "should get new" do
get :new
assert_response :success
end
test "should get edit" do
get :edit
assert_response :success
end
end
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'
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]
resources :microposts, only: [:create, :destroy]
end
Controller file
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update] # Case (1)
def new
end
def create
#user = User.find_by(email: params[:password_reset][:email].downcase)
if #user
#user.create_reset_digest
#user.send_password_reset_email
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty?
#user.errors.add(:password, "can't be empty")
render 'edit'
elsif #user.update_attributes(user_params)
log_in #user
#user.update_attribute(:reset_digest, nil)
flash[:success] = "Password has been reset."
redirect_to #user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
# Before filters
def get_user
#user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (#user && #user.activated? &&
#user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if #user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
Here's rake routes file - if I read this right there's an edit option in there ... also the editing of the route file when I remove the resource gets a different error message.
rake routes
Prefix Verb URI Pattern Controller#Action
root GET / static_pages#home
help GET /help(.:format) static_pages#help
about GET /about(.:format) static_pages#about
contact GET /contact(.:format) static_pages#contact
signup GET /signup(.:format) users#new
login GET /login(.:format) sessions#new
POST /login(.:format) sessions#create
logout DELETE /logout(.:format) sessions#destroy
users GET /users(.:format) users#index
POST /users(.:format) users#create
new_user GET /users/new(.:format) users#new
edit_user GET /users/:id/edit(.:format) users#edit
user GET /users/:id(.:format) users#show
PATCH /users/:id(.:format) users#update
PUT /users/:id(.:format) users#update
DELETE /users/:id(.:format) users#destroy
edit_account_activation GET /account_activations/:id/edit(.:format) account_activations#edit
password_resets POST /password_resets(.:format) password_resets#create
new_password_reset GET /password_resets/new(.:format) password_resets#new
edit_password_reset GET /password_resets/:id/edit(.:format) password_resets#edit
password_reset PATCH /password_resets/:id(.:format) password_resets#update
PUT /password_resets/:id(.:format) password_resets#update
microposts POST /microposts(.:format) microposts#create
micropost DELETE /microposts/:id(.:format) microposts#destroy
I think you should be passing the required parameter with the request as below:
test "should get edit" do
get :edit, { id: <id-of-the-resource-for-your-controller> }
assert_response :success
end
Instead of get :edit, try get '/password_resets/new'
To check the route path, run rake routes in terminal and see
equivalent paths for :edit and :new actions.
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...