Requiring logged-in users
為了要實現為登入的使用者必須要先登入,才能瀏覽有權限的頁面,我們要在 Users controller 使用 before filter。
before filter 透過 before_action 方法設定,指定在某個 action 執行之前調用一個方法。為了實現要求使用者先登入的限制,我們要定義一個 logged_in_user 方法,然後在 before_action 裡面調用這個方法:
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
.
.
.
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
在預設中,before filter 會應用在 controller 裡所有的 action 上,所以這邊我們傳入了 :only 參數,限制 filter 只作用在 :edit 和 :update action 上。
現在如果你嘗試瀏覽 /users/1/edit,會得到以下結果:

現在執行測試會發生錯誤,因為 edit 和 update action 現在需要使用者先登入,但相對應的測試中沒有已登入的使用者。
所以現在要來修改測試,在瀏覽 edit 和 update action 之前,要先登入使用者,我們可以使用之前定義的 log_in_as 輔助方法來實現:
test/integration/users_edit_test.rb
require 'test_helper'
class UsersEditTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "unsuccessful edit" do
log_in_as(@user)
get edit_user_path(@user)
.
.
.
end
test "successful edit" do
log_in_as(@user)
get edit_user_path(@user)
.
.
.
end
end
現在測試應該會通過(Green):
$ bundle exec rake test
雖然測試通過了,但如果把剛剛的 before_action 註解掉,測試還是會通過,這可不妙:
app/controllers/users_controller.rb
class UsersController < ApplicationController
# before_action :logged_in_user, only: [:edit, :update]
.
.
.
end
註解後,測試應該不能通過,所以我們要編寫測試捕捉這個問題。
因為 before filter 是應用在每個指定的 action 上,所以要在 Users controller test 撰寫相對應的測試。
計畫是使用正確的請求方法瀏覽 edit 和 update action,然後檢查 flash 有出現,最後確認有把使用者導向登入頁面。查詢 rake routes 可以知道請求的方法分別是 GET 和 PATCH。
完整的測試程式碼如下:
test/controllers/users_controller_test.rb
require 'test_helper'
class UsersControllerTest < ActionController::TestCase
def setup
@user = users(:michael)
end
test "should get new" do
get :new
assert_response :success
end
test "should redirect edit when not logged in" do
get :edit, id: @user
assert_not flash.empty?
assert_redirected_to login_url
end
test "should redirect update when not logged in" do
patch :update, id: @user, user: { name: @user.name, email: @user.email }
assert_not flash.empty?
assert_redirected_to login_url
end
end
注意 get 和 patch 的參數:
get :edit, id: @user
和
patch :update, id: @user, user: { name: @user.name, email: @user.email }
這邊使用 Rails 的慣例,設定 id: @user 時,Rails 會自動使用 @user.id。在 patch 方法中,還要指定一個 user hash,這樣路由才會正常運作。
現在執行測試會無法通過,這正是我們要的結果。
然後把註解拿掉:
app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:edit, :update]
.
.
.
end
再執行測試,就會通過了(Green):
$ bundle exec rake test
如果不小心讓未授權的使用者瀏覽 edit action,現在測試能立即捕捉。