I’m reading the Michael Hartl’s “Ruby on Rails “Turorial” but I’m stuck here.
I wrote the tests to check user authorization on user edit/update, I’ve also wrote the controller code and it works:
- If user is not logged and try to access
users#editorusers#updateit’s redirected to the login page - If the user is logged and he try to edit another user is redirected to the root
The problem is in the spec that tests the point 2, I check that sending a PUT request to the user_path I’m redirected to the root but running rspec I get the following error:
Failures:
1) Authentication authorization as wrong user submitting a PUT request to users#update
Failure/Error: specify { response.should redirect_to(root_url) }
Expected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/signin>
# ./spec/requests/authentication_pages_spec.rb:73:in `block (5 levels) in <top (required)>'
It seems it’s redirected to the signin_url instead of the root_url, like if the user is not logged at all…and this is what I guess cause the problem.
The application store a token in the cookies to authenticate the user.
To test this I wrote an helper method called signin_user(user) and put it in the spec/support/utilities.rb file:
# spec/support/utilities.rb
def sign_in(user)
visit signin_path
fill_in "Email", with: user.email
fill_in "Password", with: user.password
click_button "Sign in"
# Make signin work even when not using capybara (e.g. when using get, or post)
cookies[:remember_token] = user.remember_token
end
As you can see the method set the remember_token cookie to make sure the user is logged even when using put, delete, etc…or at least it should work!
How can I make this spec pass as it should?
# spec/requests/authentication_pages_spec.rb
require 'spec_helper'
describe "Authentication" do
subject { page }
# ...
describe "authorization" do
# ...
describe "as wrong user" do
let(:user) { FactoryGirl.create(:user) }
let(:another_user) { FactoryGirl.create(:user, email: "another@example.com") }
before { sign_in(user) }
# ...
describe "submitting a PUT request to users#update" do
before { put user_path(another_user) }
specify { response.should redirect_to(root_url) }
end
end
end
end
UPDATE: And this is my UsersController:
class UsersController < ApplicationController
before_filter :signed_in_user, :only => [: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 sample App!"
redirect_to @user
else
render "new"
end
end
def edit
end
def update
if @user.update_attributes(params[:user])
sign_in(@user)
flash[:success] = "Your profile was successfully updated"
redirect_to @user
else
render "edit"
end
end
private
def signed_in_user
unless signed_in?
redirect_to signin_url, notice: "Please sign in to access this page."
end
end
def correct_user
@user = User.find(params[:id])
redirect_to root_url unless current_user?(@user)
end
end
After looking at the helpers from the rest of the code on your github repository I think I see the problem.
You’re using the
sessionvariable for storing your:remember_token:The book uses cookies:
Your test helper sets the cookie:
but because your
user_from_remember_tokenis looking forsession[:remember_token]not a cookie, it thinks the user isn’t logged in so the test gets redirected tologin_pathinstead ofroot_path.It says why they use a cookie here: http://ruby.railstutorial.org/chapters/sign-in-sign-out?version=3.2#sec:a_working_sign_in_method
If you want to continue using a session you would need to adjust your test to sign in via
postso theputhas the session set, for example: