Rails API应用使用devise时无法编写rspec登录请求测试用例如何解决
问题根因
你当前的测试无法通过的核心原因是:Devise提供的sign_in测试辅助方法是测试层直接注入登录态,不会走实际的SessionsController#create登录接口逻辑,自然不会触发devise-jwt的token签发逻辑,也不会生成响应对象,所以你读取的response是空值。
可行解决思路
1. 修正登录接口测试逻辑
如果你的测试目标是验证登录接口本身的返回(token、状态码),不要用sign_in辅助方法,而是模拟真实请求调用登录接口:
context 'When logging in' do let(:user) { create(:user, password: 'testpass123') } # 确保factory中密码设置和这里一致 it 'returns a valid token and 200 status' do # 替换为你实际的登录路由,参数结构要符合你接口要求 post '/users/sign_in', params: { user: { email: user.email, password: 'testpass123' } }, as: :json expect(response.status).to eq(200) expect(response.headers['Authorization']).to be_present expect(response.headers['Authorization']).to start_with('Bearer ') end end
注意:request类型测试不需要设置request.env["devise.mapping"],该配置仅用于controller单元测试
2. 检查devise-jwt基础配置
如果调用接口后还是没有返回token,确认以下配置是否正确:
- 检查
config/initializers/devise.rb中是否正确配置了jwt相关参数:config.jwt do |jwt| jwt.secret = Rails.application.credentials.devise_jwt_secret_key jwt.dispatch_requests = [ ['POST', %r{^/users/sign_in$}] # 路径要和你的登录路由完全匹配 ] jwt.revocation_requests = [ ['DELETE', %r{^/users/sign_out$}] ] end - 确认User模型已引入jwt模块:
class User < ApplicationRecord devise :database_authenticatable, :registerable, :jwt_authenticatable, jwt_revocation_strategy: JwtDenylist # 按你实际的吊销策略调整 end - 确认API模式下已关闭默认导航跳转:
config.navigational_formats = [],避免devise尝试跳转HTML页面导致接口返回302
3. 如需测试其他需要登录态的接口
如果你的目标是测试其他需要登录才能访问的接口,不需要验证登录接口本身,有两种实现方式:
- 用
sign_in辅助方法直接注入登录态,不需要处理jwt - 若接口强制要求校验jwt请求头,可手动生成token放到请求头:
let(:user) { create(:user) } let(:auth_headers) { token = Warden::JWTAuth::UserEncoder.new.call(user, :user, nil).first { 'Authorization' => "Bearer #{token}" } } it 'accesses protected route successfully' do get '/protected/route', headers: auth_headers, as: :json expect(response.status).to eq(200) end
内容的提问来源于stack exchange,提问作者user17100810
相关产品推荐
相关产品推荐

