使用Devise/Rspec测试用户登录访问路由时遇before方法报错问题
解决Devise/Rspec控制器测试中login_user宏的错误问题
这个错误的核心原因是你在example内部(it块里)调用了包含before(:each)钩子的宏,而RSpec明确规定before钩子只能在example group(describe/context块)的作用域里定义,不能在单个example里使用。
方案一:修改宏为直接执行登录逻辑
把你的controller_macros里的login_user改成直接执行登录代码,而不是包裹在before(:each)里:
# spec/support/controller_macros.rb def login_user @request.env["devise.mapping"] = Devise.mappings[:user_confirmed] @user = FactoryBot.create(:user_confirmed) sign_in @user end def login_admin @request.env["devise.mapping"] = Devise.mappings[:admin] @user = FactoryBot.create(:admin) sign_in @user end
然后在测试里,你可以选择在before块里(作用于整个group)或者直接在it块里调用:
require "rails_helper" RSpec.describe BallotsController, type: :controller do describe "index" do # 方式1:在before块里调用,作用于当前describe下的所有example before do login_user end it "renders successfully" do # 注意:控制器测试需要用get方法发起请求,不是直接写ballots_path get :index expect(response).to be_success expect(response).to render_template("index") end end end
或者直接在单个example里调用:
it "renders successfully" do login_user get :index expect(response).to be_success expect(response).to render_template("index") end
方案二:保持宏的钩子写法,但在group层面调用
如果你想保留宏里的before(:each)逻辑(让它自动作用于整个group的所有example),那你需要在describe/context块里调用login_user,而不是在it块里:
require "rails_helper" RSpec.describe BallotsController, type: :controller do describe "index" do # 在describe块里调用,这样before(:each)会应用到当前group下的所有example login_user it "renders successfully" do get :index expect(response).to be_success expect(response).to render_template("index") end end end
额外注意点
你的测试代码里还有一个小问题:ballots_path只是生成路由字符串,不会触发控制器的请求。在RSpec控制器测试中,需要用get :index(或者对应HTTP方法的方法,比如post、put等)来模拟发起请求,这样才能得到response对象进行断言。
内容的提问来源于stack exchange,提问作者lylesconstant
相关产品推荐
相关产品推荐

