Rails 7 API模式测试中手动设置User.current失效问题求助
问题原因
Rails集成测试(ActionDispatch::IntegrationTest)的每个请求都会在全新的线程中处理,你在测试主线程设置的User.current不会被共享到请求处理的线程中。如果User.current是基于类变量(如@@current)实现的,多线程环境下还会出现变量被意外重置的问题。
解决方案
1. 修复User.current的实现(改用线程局部变量)
确保User.current使用线程局部存储,避免多线程冲突:
class User < ApplicationRecord def self.current=(user) Thread.current[:current_user] = user end def self.current Thread.current[:current_user] end end
2. 在测试请求中传递用户标识,在控制器中设置User.current
修改测试用例,通过请求头传递测试用户ID:
require 'test_helper' class ThingControllerTest < ActionDispatch::IntegrationTest test "call method my_method" do me = users(:me) get "/api/users/#{me.id}/do-the-thing", headers: { 'X-Test-User-Id' => me.id.to_s } assert_response :success end end
更新ApplicationController的认证逻辑,在测试环境下读取请求头并设置User.current:
class ApplicationController < ActionController::Base before_action :authenticate_user def authenticate_user if Rails.env.test? if test_user_id = request.headers['X-Test-User-Id'] User.current = User.find(test_user_id) end return User.current end # 非测试环境的认证逻辑 end end
替代方案:使用测试辅助方法封装
如果需要在多个测试中复用,可以在test_helper.rb中添加辅助方法:
class ActionDispatch::IntegrationTest private def headers_with_current_user(user) { 'X-Test-User-Id' => user.id.to_s } end end
然后测试用例可以简化为:
test "call method my_method" do me = users(:me) get "/api/users/#{me.id}/do-the-thing", headers: headers_with_current_user(me) assert_response :success end
验证
修改后,请求处理线程会通过请求头获取用户ID并设置User.current,此时ThingController#the_thing中的User.current就能正确获取到测试用户,不会再为nil。
内容的提问来源于stack exchange,提问作者OST
相关产品推荐
相关产品推荐

