[英]Rails & MiniTest IntegrationTests - session not accessible after redirect
顯然,Rails 已棄用 ControllerTest,轉而支持 IntegrationTest。 我想根據登錄用戶測試行為。 我無法直接設置當前用戶,必須先使用應用程序的登錄流程,然后才能測試感興趣的路線和行為。 很好,但是在 session 中負責處理登錄和設置詳細信息的 controller 完成其工作后,session 似乎消失了!
作為一個簡單的測試,我有一個帶有 WelcomeController 的基本 Rails 應用程序,它的顯示視圖將根據用戶的當前狀態顯示“已登錄”或“已注銷”(確定 session 是否設置了 user_id 以及該 ID 是否對應到數據庫中的記錄)。
登錄系統由 OmniAuth 處理。 這很容易模擬,我創建了一個由 OmniAuth 處理程序返回的用戶。 登錄詳細信息由SessionsController
處理,它將顯示一個表單,該表單通過郵寄方式將 email 和密碼發送到SessionsController.create
(此登錄操作的路由是/auth/identity/callback
)。
# Highly simplified logic for the login handler
class SessionsController < ApplicationController
def create
auth_hash = request.env['omniauth.auth']
user = User.find_with_omniauth(auth_hash)
session[:user_id] = user.id
Rails.logger.debug(session.inspect)
redirect_to root_path
end
end
class WelcomeController < ApplicationController
def show
Rails.logger.debug(session.inspect)
end
end
class ApplicationController < ActionController::Base
def current_user
@current_user ||= if session.key?(:user_id)
User.find_by(id: session[:user_id])
else
nil
end
end
def logged_in?
current_user.present?
end
end
現在進行測試:
require 'test_helper'
class WelcomeControllerTest < ActionDispatch::IntegrationTest
include FactoryBot::Syntax::Methods
setup do
OmniAuth.config.test_mode = true
end
teardown do
OmniAuth.config.test_mode = false
OmniAuth.config.mock_auth[:identity] = nil
end
def manual_identity_log_in(user_with_identity)
OmniAuth.config.mock_auth[:identity] = OmniAuth::AuthHash.new(
{
provider: 'identity',
uid: user_with_identity.id,
info: {
email: user_with_identity.email,
first_name: user_with_identity.first_name,
last_name: user_with_identity.last_name,
name: user_with_identity.full_name
}
})
post '/auth/identity/callback', params: {email: user_with_identity.email, password: ''}
end
test "can see the landing page for logged out users" do
get "/"
assert_select( "h1", "Logged Out")
end
test "can see the welcome page for logged in users" do
current_user = create(:user_with_identity)
manual_identity_log_in(current_user)
get "/"
assert_select( "h1", "Logged In")
end
end
測試失敗 - 它始終顯示“已注銷”消息。
注意:如果我在SessionsController.create
末尾將 session 打印到日志中,我可以得到我期望的一切 - 登錄已成功並且已全部設置。 但是記錄在WelcomeController
中的 session 實例,由測試中隨后的get "/"
調用觸發,是#<ActionDispatch::Request::Session:0x3b880 not yet loaded>
。 對我來說,這顯然是一個失敗,因為 session 沒有在兩個請求之間保留。 WelcomeController,在上下文中,如果此測試將始終確定要注銷的用戶。
盡管進行了無休止的搜索和閱讀,但在我看來,我一定遺漏了一些非常明顯的東西,因為這是 IntegrationTests 的預期方法。
TIA
我找到了解決方案,它與 session 商店配置有關。 這可能有點“陷阱”
在開發 Rails 應用程序並在開發模式下運行時,我仍然使用自簽名證書以便在 https 上運行本地主機。當然,當應用程序部署到暫存和生產環境時,它也通過 https 運行。 因此,我有一個通用的初始化程序,用於所有僅針對 https 保護 session 的環境:
# config/initializers/session_store.rb
Rails.application.config.session_store(
:cookie_store,
key: '_myapp_session',
httponly: true,
secure: true # <--- the problem
)
但是測試沒有使用 https ,所以這個 cookie 配置根本不會通過 cookie 持久化 session 。
我的解決方案是將測試模式的secure
設置為false
:
# config/initializers/session_store.rb
Rails.application.config.session_store(
:cookie_store,
key: '_myapp_session',
httponly: true,
secure: !Rails.env.test?
)
現在 session 在兩個單獨的請求中持久存在並且測試完成。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.