如何在Rspec中Stub JWT认证逻辑里的current_user?
如何在Rails控制器测试中绕过JWT认证并设置@current_user
你遇到的核心问题是:测试中@current_user始终为nil,因为你之前的Stub方式只是让authorize方法返回用户对象,并没有在控制器实例中实际设置@current_user变量,而控制器的create方法依赖这个实例变量完成逻辑。
以下是几种可行的解决方案:
方案1:跳过认证前置动作并直接设置@current_user
这是最直接的方式,完全绕过authorize的执行,直接给控制器实例注入@current_user:
require 'rails_helper' RSpec.describe BooksController do let(:current_user) { User.create(username: "Joe") } before :each do # 跳过authorize前置验证动作 controller.skip_before_action :authorize # 给控制器实例设置@current_user变量 controller.instance_variable_set(:@current_user, current_user) end context 'book creation' do it 'authenticated user can create book' do post :create, params: { book: { name: "Some Book" } } expect(response).to have_http_status(:created) end end end
方案2:Stub authorize方法让它设置@current_user
如果你不想完全跳过authorize方法,可以Stub它的执行逻辑,让它直接完成@current_user的设置:
require 'rails_helper' RSpec.describe BooksController do let(:current_user) { User.create(username: "Joe") } before :each do allow(controller).to receive(:authorize) do # 在Stub的authorize方法中设置@current_user controller.instance_variable_set(:@current_user, current_user) end end context 'book creation' do it 'authenticated user can create book' do post :create, params: { book: { name: "Some Book" } } expect(response).to have_http_status(:created) end end end
方案3:Stub第三方JWT验证逻辑(保留authorize执行)
如果你希望保留authorize方法的完整执行流程,只是绕过第三方API调用,可以模拟验证响应结果:
require 'rails_helper' RSpec.describe BooksController do let(:current_user) { User.create(username: "Joe") } before :each do # 模拟第三方验证的响应结果,包含对应用户ID mock_validation_response = double( decoded_token: [[{ "sub" => current_user.id }]], error: nil ) # Stub客户端的验证方法,返回模拟结果 allow_any_instance_of(Secured).to receive(:client).and_return( double(verify_token_from_third_party: mock_validation_response) ) end context 'book creation' do it 'authenticated user can create book' do post :create, params: { book: { name: "Some Book" } } expect(response).to have_http_status(:created) end end end
额外需要修正的代码问题
除了测试代码,你的控制器代码存在两处拼写错误,会导致测试失败:
- 控制器类名错误:
BookssController应改为BooksController - 实例变量名不一致:
create方法中初始化的是@books,后续却用@book调用save,需统一为@book:
# 修正后的create方法 def create @book = Book.new(book_params.merge({user_id: @current_user.id})) if @book.save render json: @book, status: :created, location: @book else render json: @book.errors, status: :unprocessable_entity end end
内容的提问来源于stack exchange,提问作者User089723
相关产品推荐
相关产品推荐

