测试 rspec .consider_all_requests_local = false
Testing with rspec .consider_all_requests_local = false
我在 application_controller 中使用:.consider_all_requests_local
喜欢
unless Rails.application.config.consider_all_requests_local
rescue_from ActionController::InvalidCrossOriginRequest, :with => :render_404
end
这个return 404 if ActionController::InvalidCrossOriginRequest 被引发。在本地环境中不会引发,有利于调试。
对于这部分,它正在工作。但我想用 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 的方法。
第二个问题,before 不会改变 Rails.application.config.consider_all_requests_local
状态。
我得到:
1) ApplicationController ActionController::InvalidCrossOriginRequest render 404 status
Failure/Error: raise ActionController::InvalidCrossOriginRequest
ActionController::InvalidCrossOriginRequest:
ActionController::InvalidCrossOriginRequest
尝试模拟它而不是设置:
before { Rails.stub_chain('application.config.consider_all_requests_local').and_return(false) }
更多信息here
此语法已弃用,因此您可以关闭弃用警告或使用新的 'workaround'
allow(object).to receive_message_chain(:one, :two, :three).and_return(:four)
expect(object.one.two.three).to eq(:four)
已发布 here
问题似乎是由您在 class 加载时执行的 unless
检查引起的。这意味着第一次加载 class 时会检查应用程序配置中的值,并且 rescue_from
已设置或未设置。
在最基本的解决方法中,您需要使用 load
来在设置更改后重新读取该文件。但是,原样,一旦 rescue_from
打开,再次加载文件不会导致它关闭。
下一个替代方法是使用 rescue_from(with:)
which delegates to a helper or 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
我也曾在我的 rescue_from
配置周围有一个守卫,例如:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :render_error
…
end
... 工作正常,直到我试图弄清楚如何让它处理错误并在某些 测试[=50= 中显示漂亮的自定义错误页面(就像在生产中一样) ]. @Aaron K 的 有助于解释为什么无法在 class 定义中评估检查,而必须在实际错误处理程序中(在 运行 时间)进行检查。但这对我来说只解决了部分问题。
这是我所做的...
在 ApplicationController
中,如果 show_detailed_exceptions
标志(比 consider_all_requests_local
更合适的检查)为真,请记住重新引发任何错误。换句话说,如果 app/request 配置为处理生产错误,则只进行生产错误处理;否则 "pass" 并重新引发错误。
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.9
、rails 5.2
我在 application_controller 中使用:.consider_all_requests_local
喜欢
unless Rails.application.config.consider_all_requests_local
rescue_from ActionController::InvalidCrossOriginRequest, :with => :render_404
end
这个return 404 if ActionController::InvalidCrossOriginRequest 被引发。在本地环境中不会引发,有利于调试。 对于这部分,它正在工作。但我想用 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 的方法。
第二个问题,before 不会改变 Rails.application.config.consider_all_requests_local
状态。
我得到:
1) ApplicationController ActionController::InvalidCrossOriginRequest render 404 status
Failure/Error: raise ActionController::InvalidCrossOriginRequest
ActionController::InvalidCrossOriginRequest:
ActionController::InvalidCrossOriginRequest
尝试模拟它而不是设置:
before { Rails.stub_chain('application.config.consider_all_requests_local').and_return(false) }
更多信息here
此语法已弃用,因此您可以关闭弃用警告或使用新的 'workaround'
allow(object).to receive_message_chain(:one, :two, :three).and_return(:four)
expect(object.one.two.three).to eq(:four)
已发布 here
问题似乎是由您在 class 加载时执行的 unless
检查引起的。这意味着第一次加载 class 时会检查应用程序配置中的值,并且 rescue_from
已设置或未设置。
在最基本的解决方法中,您需要使用 load
来在设置更改后重新读取该文件。但是,原样,一旦 rescue_from
打开,再次加载文件不会导致它关闭。
下一个替代方法是使用 rescue_from(with:)
which delegates to a helper or 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
我也曾在我的 rescue_from
配置周围有一个守卫,例如:
unless Rails.application.config.consider_all_requests_local
rescue_from Exception, with: :render_error
…
end
... 工作正常,直到我试图弄清楚如何让它处理错误并在某些 测试[=50= 中显示漂亮的自定义错误页面(就像在生产中一样) ]. @Aaron K 的
这是我所做的...
在 ApplicationController
中,如果 show_detailed_exceptions
标志(比 consider_all_requests_local
更合适的检查)为真,请记住重新引发任何错误。换句话说,如果 app/request 配置为处理生产错误,则只进行生产错误处理;否则 "pass" 并重新引发错误。
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.9
、rails 5.2