You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails集成测试Devise登录场景下如何成功设置session[:selected_section]

问题根因

你的实现存在两个核心问题导致session无法写入:

  • Devise默认的登录请求要求用户身份凭证放在user嵌套参数下,你直接把user_id放在params顶层会触发Devise验证失败,无法生成有效用户会话,自然不会执行后续的session写入逻辑
  • 如果你在测试中需要多会话隔离,没有显式持有open_session返回的会话实例的话,不同请求的session上下文互相隔离,你读取的session和发起登录请求的session不属于同一个上下文,自然返回nil

修复实现

步骤1:重写Devise会话控制器,写入section参数

你需要先让后端逻辑支持接收selected_section参数并写入session,Rails不会自动完成这个操作:

# app/controllers/users/sessions_controller.rb
class Users::SessionsController < Devise::SessionsController
  def create
    # 登录成功回调中写入session
    super do |resource|
      session[:selected_section] = params[:selected_section] if params[:selected_section].present?
    end
  end
end

修改路由配置,让Devise使用你重写的会话控制器:

# config/routes.rb
devise_for :users, controllers: { sessions: 'users/sessions' }

步骤2:修改测试工具方法

单会话测试场景(大部分常规测试用例适用)

# test_helper.rb
def sign_in_with_section_id_as(user, section_id: 31)
  post user_session_url, params: {
    user: {
      # *Devise默认使用email作为登录验证字段,如果你要改用id需要在Devise初始化配置中修改 authentication_keys = [:id]*
      id: user.id,
      # *必须传入正确的用户密码,否则Devise验证失败无法登录*
      password: 'your_test_user_password'
    },
    selected_section: section_id
  }
  # 可选:增加断言提前发现登录失败问题
  assert_response :redirect
  assert_equal user.id, session[:user_id]
end

# 测试用例中直接调用即可
sign_in_with_section_id_as(roleshopuser.user)
puts session[:selected_section] # 输出31

多会话隔离测试场景(同时模拟多个用户登录适用)

需要使用open_session显式持有不同用户的会话实例,避免上下文冲突:

# test_helper.rb
def sign_in_with_section_id_as(user, session_instance, section_id: 31)
  session_instance.post user_session_url, params: {
    user: {
      id: user.id,
      password: 'your_test_user_password'
    },
    selected_section: section_id
  }
end

# 测试用例用法
test "multi user session test" do
  user1_session = open_session
  user2_session = open_session

  sign_in_with_section_id_as(roleshopuser.user, user1_session)
  puts user1_session.session[:selected_section] # 输出31

  sign_in_with_section_id_as(other_user, user2_session, section_id: 32)
  puts user2_session.session[:selected_section] # 输出32
end

排查 Checklist

如果修改后仍然返回nil,可以按以下顺序排查:

  • 确认Devise初始化配置中authentication_keys和你传入的登录字段一致,默认是:email而非:id
  • 确认登录请求返回状态码是302重定向,如果返回200说明登录验证失败,表单被重新渲染
  • 确认你没有在测试中关闭CSRF保护,集成测试默认会自动携带CSRF令牌,手动关闭后需要额外传入authenticity_token参数

内容的提问来源于stack exchange,提问作者Jerome

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 17:06:00