使用rspec .consider_all_requests_local

时间:2015-06-29 07:45:23

标签: ruby-on-rails ruby rspec tdd rspec-rails

我在 application_controller .consider_all_requests_local中使用

  unless Rails.application.config.consider_all_requests_local
    rescue_from ActionController::InvalidCrossOriginRequest, :with => :render_404
  end

如果引发ActionController :: InvalidCrossOriginRequest,则返回404。在本地环境中它没有被提升,它对调试很有用。 对于这部分它是有效的。但我想用rspec测试它。

我尝试过像

这样的东西
describe 'ActionController::InvalidCrossOriginRequest render 404' do
    before { Rails.application.config.consider_all_requests_local = false }
    controller do
      def index
        raise ActionController::InvalidCrossOriginRequest
      end
    end

    subject { xhr :get, :index, format: :js }

    its(:status) { is_expected.to eq 404 }
end

两件事。我可能不会以正确的方式提高。在本地调用 mywebsite.com/editor/fckeditor.js 时会发生错误。没有找到一种方法来调用特定的URL。

第二个问题,之前的不会改变Rails.application.config.consider_all_requests_local状态。

我明白了:

1) ApplicationController ActionController::InvalidCrossOriginRequest render 404 status
     Failure/Error: raise ActionController::InvalidCrossOriginRequest
     ActionController::InvalidCrossOriginRequest:
       ActionController::InvalidCrossOriginRequest

3 个答案:

答案 0 :(得分:2)

该问题看起来是由您在课程加载时执行unless检查引起的。这意味着第一次加载类时,将检查应用程序配置中的值,并设置或不设置rescue_from

在最基本的解决方法中,您需要使用load以便在更改设置后重新读取该文件。但是,一旦rescue_from开启,再次加载文件不会导致文件关闭。

下一个选择是使用rescue_from(with:)委托给帮助者或the block form。您可以使用此帮助程序检查值并处理条件。但是,考虑到这只是你想在非生产环境中做的事情,你可以将两者结合起来。使用unless验证您是否处于生产阶段,然后每次都使用with来检查配置。

类似的东西:

class ApplicationController < ActionController::Base
  unless Rails.env.production?
    rescue_from ActionController::InvalidCrossOriginRequest do
      unless Rails.application.config.consider_all_requests_local
        render_404
      end
    end
  end
end

答案 1 :(得分:1)

尝试嘲笑它而不是设置:

before { Rails.stub_chain('application.config.consider_all_requests_local').and_return(false) }

更多信息here

不推荐使用此语法,因此您可以关闭弃用警告或使用新的“解决方法”

allow(object).to receive_message_chain(:one, :two, :three).and_return(:four)
expect(object.one.two.three).to eq(:four)

已发布here

答案 2 :(得分:0)

我以前也对rescue_from配置进行保护,例如:

unless Rails.application.config.consider_all_requests_local
  rescue_from Exception, with: :render_error
  …
end

...效果很好,直到我试图弄清楚如何使其能够处理错误并在某些 tests 中显示漂亮的自定义错误页面(就像在生产中一样)。 @Aaron K的{​​{3}}有助于解释为什么不能在类定义中评估检查,而必须在实际的错误处理程序中(在运行时)进行检查。但这对我来说只能解决部分问题。

这就是我所做的...

ApplicationController中,请记住如果show_detailed_exceptions标志(比consider_all_requests_local更合适的检查)为true,则重新引发所有错误。换句话说,只有将应用程序/请求配置为处理生产错误时,才进行生产错误处理;否则“通过”并重新引发错误。

  rescue_from Exception,                           with: :render_error
  rescue_from ActiveRecord::RecordNotFound,        with: :render_not_found
  rescue_from ActionController::RoutingError,      with: :render_not_found
  rescue_from AbstractController::ActionNotFound,  with: :render_not_found

  def show_detailed_exceptions?
    # Rails.application.config.consider_all_requests_local causes this to be set to true as well.
    request.get_header("action_dispatch.show_detailed_exceptions")
  end

  def render_not_found(exception = nil, template = 'errors/not_found')
    raise exception if show_detailed_exceptions?
    logger.error exception if exception
    render template, formats: [:html], status: :not_found
  end

  def render_error(exception)
    raise exception if show_detailed_exceptions?
    deliver_exception_notification(exception)
    logger.error exception

    # Prevent AbstractController::DoubleRenderError in case we've already rendered something
    method(:response_body=).super_method.call(nil)

    respond_to do |format|
      format.html { render 'errors/internal_server_error', formats: [:html], status: :internal_server_error }
      format.any  { raise exception }
    end
  end

添加到spec/support/handle_exceptions_like_production.rb

shared_context 'handle_exceptions_like_production', handle_exceptions_like_production: true do
  before do |example|
    case example.metadata[:type]
    when :feature
      method = Rails.application.method(:env_config)
      allow(Rails.application).to receive(:env_config).with(no_args) do
        method.call.merge(
          'action_dispatch.show_exceptions' => true,
          'action_dispatch.show_detailed_exceptions' => false,
          'consider_all_requests_local' => true
        )
      end
    when :controller
      # In controller tests, we can only test *controller* behavior, not middleware behavior.  We
      # can disable show_detailed_exceptions here but we can *only* test any behaviors that depend
      # on it that are defined in our *controller* (ApplicationController). Because the request
      # doesn't go through the middleware (DebugExceptions, ShowExceptions) — which is what actually
      # renders the production error pages — in controller tests, we may not see the exact same
      # behavior as we would in production. Feature (end-to-end) tests may be needed to more
      # accurately simulate a full production stack with middlewares.
      request.set_header 'action_dispatch.show_detailed_exceptions', false
    else
      raise "expected example.metadata[:type] to be one of :feature or :controller but was #{example.metadata[:type]}"
    end
  end
end

RSpec.configure do |config|
  config.include_context 'handle_exceptions_like_production', :handle_exceptions_like_production
end

然后,在端到端(功能)测试中,您希望它像生产环境中一样处理异常(换句话说,将其视为本地请求),将:handle_exceptions_like_production添加到您的示例组:

describe 'something', :handle_exceptions_like_production do
  it …
end

例如:

spec/features/exception_handling_spec.rb

describe 'exception handling', js: false do
  context 'default behavior' do
    it do |example|
      expect(example.metadata[:handle_exceptions_like_production]).to eq nil
    end

    describe 'ActiveRecord::RecordNotFound' do
      it do
        expect {
          visit '/users/0'
        }.to raise_exception(ActiveRecord::RecordNotFound)
      end
    end

    describe 'ActionController::RoutingError' do
      it do
        expect {
          visit '/advertisers/that_track_you_and_show_you_personalized_ads/'
        }.to raise_exception(ActionController::RoutingError)
      end
    end

    describe 'RuntimeError => raised' do
      it do
        expect {
          visit '/test/exception'
        }.to raise_exception(RuntimeError, 'A test exception')
      end
    end
  end

  context 'when :handle_exceptions_like_production is true', :handle_exceptions_like_production do
    describe 'ActiveRecord::RecordNotFound => production not_found page' do
      it do
        expect {
          visit '/users/0'
        }.to_not raise_exception
        expect_not_found
      end
    end

    describe 'ActionController::RoutingError => production not_found page' do
      it do
        visit '/advertisers/that_track_you_and_show_you_personalized_ads/'
        expect_not_found
      end
    end

    describe 'RuntimeError => production not_found page' do
      it do
        visit '/test/exception'
        expect_application_error
      end
    end
  end
end

如果在ApplicationController中定义了生产错误处理,它也可以用于控制器测试。 spec/controllers/exception_handling_spec.rb

describe 'exception handling' do
  context 'default behavior' do
    describe UsersController do
      it do
        expect {
          get 'show', params: {id: 0}
        }.to raise_exception(ActiveRecord::RecordNotFound)
      end
    end

    describe TestController do
      it do
        expect {
          get 'exception'
        }.to raise_exception(RuntimeError, 'A test exception')
      end
    end
  end

  context 'when handle_exceptions_like_production: true', :handle_exceptions_like_production do
    describe UsersController do
      it do
        expect {
          get 'show', params: {id: 0}
        }.to_not raise_exception
        expect(response).to render_template('errors/not_found')
      end
    end

    describe TestController do
      it do
        expect {
          get 'exception'
        }.to_not raise_exception
        expect(response).to render_template('errors/internal_server_error')
      end
    end
  end
end

经过以下测试:rspec 3.9rails 5.2