簡體   English   中英

Rails & MiniTest IntegrationTests - session 重定向后無法訪問

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM