如何使用普通控制器测试来测试"捕获所有路线"?

fea*_*ool 5 routing actioncontroller ruby-on-rails-3

注意:根据RafaeldeF.Ferreira的建议,这个问题自其原始形式以来经过大量编辑.

我的基于JSON的应用程序需要在给出错误路径时返回合理的内容.我们已经知道以下rescue_from ActionController::RoutingError在Rails 3.1和3.2中不起作用:

# file: app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery
  rescue_from ActionController::RoutingError, :with => :not_found
  ...
end
Run Code Online (Sandbox Code Playgroud)

(这在https://github.com/rails/rails/issues/671中有详细记载.)所以我实现了JoséValim在本博客条目(第3项)中描述的内容,详情如下.

但测试它一直存在问题.这个控制器rspec测试:

# file: spec/controllers/errors_controller.rb
require 'spec_helper'
require 'status_codes'
describe ErrorsController do
  it "returns not_found status" do
    get :not_found
    response.should be(StatusCodes::NOT_FOUND)
  end
end
Run Code Online (Sandbox Code Playgroud)

失败了:

ActionController::RoutingError: No route matches {:format=>"json", :controller=>"sites", :action=>"update"}
Run Code Online (Sandbox Code Playgroud)

然而,这个集成测试调用ErrorsController#not_found并成功:

# file: spec/requests/errors_spec.rb
require 'spec_helper'
require 'status_codes'

describe 'errors service' do
  before(:each) do
    @client = FactoryGirl.create(:client)
  end

  it "should catch routing error and return not_found" do
    get "/v1/clients/login.json?id=#{@client.handle}&password=#{@client.password}"
    response.status.should be(StatusCodes::OK)
    post "/v1/sites/impossiblepaththatdoesnotexist"
    response.status.should be(StatusCodes::NOT_FOUND)
  end
end
Run Code Online (Sandbox Code Playgroud)

那么:有没有办法用普通的控制器测试来测试'捕获所有路径'?

实施细节

如果您想查看实现,请参阅相关代码片段

# config/routes.rb
MyApp::Application.routes.draw do
  ... all other routes above here.
  root :to => "pages#home"
  match "/404", :to => "errors#not_found"
end

# config/application.rb
module MyApp
  class Application < Rails::Application
    config.exceptions_app = self.routes
    ...
  end
end

# config/environments/test.rb
MyApp::Application.configure do
  ...
  config.consider_all_requests_local       = false
  ...
end

# app/controllers/errors_controller.rb
class ErrorsController < ApplicationController
  def not_found
    render :json => {:errors => ["Resource not found"]}, :status => :not_found
  end
end
Run Code Online (Sandbox Code Playgroud)