如何在RSpec请求测试中测试Rails控制器关注点并配置路由?
如何在RSpec请求测试中单独测试控制器关注点?
背景
我们有如下控制器关注点:
module BillingErrorRescuer extend ActiveSupport::Concern included do rescue_from Billing::LimitExceeded, with: :rescue_billing_limits def rescue_billing_limits render status: 402, json: Billing::PlanPresenter.new(@account).to_json end end end
在控制器测试中,我们可以通过匿名控制器单独测试这个关注点:
require 'spec_helper' describe BillingErrorRescuer, type: :controller do controller do include BillingErrorRescuer def action @account = FactoryBot.create :account raise Billing::LimitExceeded end end before do routes.draw { get :action, to: "anonymous#action" } get :action end let(:account) { create :account, state: :trial } it 'renders json with billing data' do expect(response.body).to match_json_schema('controllers/billing_error_rescuer') end end
问题
现在需要在RSpec请求测试中实现同样的测试,但尝试配置路由时遇到了404错误。之前试过直接在before块中写routes.draw { get :action, to: "anonymous#action" },也用过around块修改路由,但调用get '/action'时始终返回404。
解决方案
请求测试的上下文与控制器测试不同,不能直接复用匿名控制器的写法,需要显式定义临时控制器并正确配置路由:
require 'spec_helper' describe BillingErrorRescuer, type: :request do # 显式定义临时测试控制器 class TestBillingController < ApplicationController include BillingErrorRescuer def action @account = FactoryBot.create(:account) raise Billing::LimitExceeded end end before do # 向Rails全局路由添加临时路由 Rails.application.routes.draw do get '/action', to: 'test_billing#action' end end after do # 测试结束后重置路由,避免影响其他测试用例 Rails.application.routes.reload! end it '返回包含账单数据的JSON,状态码为402' do get '/action' expect(response).to have_http_status(:payment_required) expect(response.body).to match_json_schema('controllers/billing_error_rescuer') end end
关键说明
- 不能用匿名控制器:请求测试不支持控制器测试的
controller块语法,必须显式定义控制器类(如TestBillingController),确保Rails能找到对应的控制器处理请求。 - 操作全局路由:请求测试中需要直接修改
Rails.application.routes,而不是控制器测试中的routes对象,否则路由不会生效。 - 重置路由:测试完成后必须调用
Rails.application.routes.reload!清除临时路由,防止后续测试受到影响。 - 使用完整路径:请求时要调用
get '/action',而不是符号形式的get :action,符合请求测试的URL调用规范。
内容的提问来源于stack exchange,提问作者Alexander Presber
相关产品推荐
相关产品推荐

