如何在 rails 中测试自定义的 not_found 路由

How to test a customized not_found route in rails

我有以下情况:

已编辑

在我的routes.rb

namespace :api, defaults: { format: :json } do
    namespace :v1 do
        # the definitions of other routes of my api
        # ...
        match '*path', to: 'unmatch_route#not_found', via: :all
    end
end

已编辑

我的控制器:

class Api::V1::UnmatchRouteController < Api::V1::ApiController
  def not_found
    respond_to do |format|
      format.json { render json: { error: 'not_found' }, status: 404 }
    end
  end
end

我的测试如图:

require 'rails_helper'

RSpec.describe Api::V1::UnmatchRouteController, type: :controller do
  describe 'get response from unmatched route' do
    before do
      get :not_found, format: :json
    end

    it 'responds with 404 status' do
      expect(response.status).to eq(404)
    end

    it 'check the json response' do
      expect(response.body).to eq('{"error": "not_found"}')
    end
  end
end

这对我来说似乎是正确的,但是我对两个 it 语句都得到了相同的错误:

1) Api::V1::UnmatchRouteController get response from unmatched route responds with 404 status
 Failure/Error: get :not_found, format: :json

 ActionController::UrlGenerationError:
   No route matches {:action=>"not_found", :controller=>"api/v1/unmatch_route", :format=>:json}
 # /home/hohenheim/.rvm/gems/ruby-2.3.1@dpms-kaefer/gems/gon-6.1.0/lib/gon/spec_helpers.rb:15:in `process'
 # ./spec/controllers/api/v1/unmatch_route_controller_spec.rb:14:in `block (3 levels) in <top (required)>'

已编辑

当我的 api 中没有其他可能的路由时,使用自定义 json 404 响应 会触发此路由的目的。当我们访问如下路由时,此路由和控制器现在按预期工作:/api/v1/foo/api/v1/bar

如何正确编写测试?

附加信息:Rails 4.2.6,Rspec 3.5.4

看看这个link:

https://apidock.com/rails/ActionDispatch/Routing/Mapper/Base/match

它说:

Note that :controller, :action and :id are interpreted as url query parameters and thus available through params in an action.

match ":controller/:action/:id"

您的路线是:

match '*path', to: 'unmatch_route#not_found', via: :all

所以你的测试试图找到一个路由:action=>"not_found" inside :controller=>"api/v1/unmatch_route"。但是你的routes.rb没有这条路线。

尝试这样的事情:

match 'unmatch_route/not_found', to: 'unmatch_route#not_found', via: :all

如果你真的需要使用 *path 试试这个:

match '/:path/', :to => 'unmatch_route#not_found', :path=> /.*/, :as =>'not_found'

如果您尝试编写路由规范,它也不会起作用,而且会 return 一些奇怪的东西。

 Failure/Error:
       expect(get("/unmatch")).
         to route_to("unmatch_route#not_found")
   The recognized options <{"controller"=>"unmatch_route", "action"=>"not_found", "path"=>"unmatch"}> did not match <{"controller"=>"unmatch_route", "action"=>"not_found"}>, difference:.
   --- expected
   +++ actual
   @@ -1 +1 @@
   -{"controller"=>"unmatch_route", "action"=>"not_found"}
   +{"controller"=>"unmatch_route", "action"=>"not_found", "path"=>"unmatch"}

除了操作 not_found,它 returned 路径 => 不匹配,这可能是控制器规范未按预期工作的原因。因此,您可以使用请求测试代替控制器测试,如下所示。

require 'rails_helper'

RSpec.describe "get response from unmatched route", :type => :request do
  before do
    get '/not_found', format: :json
  end

  it 'responds with 404 status' do
    expect(response.status).to eq(404)
  end

  it 'check the json response' do
    expect(response.body).to eq('{"error": "not_found"}')
  end
end

我还发现自己想测试 API 错误的响应是渲染 JSON,而不是编写一个简单地拯救 ActionController::RoutingError.

的规范

以下 request spec 对我有用,使用 Rails 6.0 & RSpec 3.9:

require 'rails_helper'

RSpec.describe '404 response for API endpoints' do
  it 'renders an error in JSON' do
    render_exceptions do
      get '/api/v1/fictional-endpoint', headers: { 'Accept' => 'application/json' }
    end

    expect(response).to have_http_status(:not_found)
    expect(response['Content-Type']).to include('application/json')
    expect(json_response.fetch(:errors)).to include('Not found')
  end

  private

  def json_response
    JSON.parse(response.body, symbolize_names: true)
  end

  def render_exceptions
    env_config = Rails.application.env_config
    original_show_exceptions = env_config['action_dispatch.show_exceptions']
    original_show_detailed_exceptions = env_config['action_dispatch.show_detailed_exceptions']
    env_config['action_dispatch.show_exceptions'] = true
    env_config['action_dispatch.show_detailed_exceptions'] = false
    yield
  ensure
    env_config['action_dispatch.show_exceptions'] = original_show_exceptions
    env_config['action_dispatch.show_detailed_exceptions'] = original_show_detailed_exceptions
  end
end

参考文献: