Rails 5 + RSpec 3.8:控制器测试中无法传递session变量的问题
解决Rails 5.2 + RSpec 3.8中控制器测试无法传递Session的问题
我之前也踩过这个坑——在Rails 5之后的RSpec控制器测试里,直接在post/get这类请求方法中传递session:参数的方式已经失效了,这就是你看到Session变量始终为nil的核心原因。
解决方案:直接操作controller.session对象
你需要在测试的before块或者具体测试用例中,直接通过controller.session来设置Session值,而不是在请求方法里附带参数。
修改后的测试代码如下:
RSpec.describe OauthCallbacksController, type: :controller do describe 'POST #confirm_email' do before do request.env["devise.mapping"] = Devise.mappings[:user] # 在这里直接给controller的session赋值 controller.session[:auth] = { uid: '12345', provider: 'facebook' } end context 'confirms_email' do let(:email) { 'test-email@test.ru' } # 移除post方法里的session参数 let(:confirm_email) { post :confirm_email, params: { email: email } } it 'redirects to new_user_session_path' do confirm_email expect(response).to redirect_to new_user_session_path end it 'sends an email' do expect { confirm_email }.to change{ ActionMailer::Base.deliveries.count }.by(1) end end end end
为什么原来的方式不行?
Rails 5重构了控制器测试的底层实现,移除了请求方法中传递session:选项的支持。现在要在控制器测试中设置Session,必须直接操作controller.session这个对象——它和你在控制器业务代码里访问的session是同一个实例。
额外的健壮性优化
为了避免控制器代码出现nil调用异常(比如session[:auth]为空时,session[:auth]['provider']会报错),可以给confirm_email方法加个前置判断:
def confirm_email puts params[:email] # 先校验session有效性 unless session[:auth].present? redirect_to root_path, alert: "Invalid session. Please try signing in again." return end pending_user = User.find_or_init_skip_confirmation(params[:email]) if pending_user aut = Authorization.where(provider: session[:auth]['provider'], uid: session[:auth]['uid'], linked_email: params[:email]) .first_or_initialize do |auth| auth.user = pending_user auth.confirmation_token = Devise.friendly_token[0, 20] auth.confirmation_sent_at = Time.now.utc end # ... 剩余逻辑保持不变 end end
内容的提问来源于stack exchange,提问作者Daria Tsheglova
相关产品推荐
相关产品推荐

