背景
exceptions_app
の仕組みで例外を捕捉しようとしているとき(*)、その処理は Rack Middleware のActionDispatch::ShowExceptions
で行われます。本番環境 (RAILS_ENV = production
) ではここで例外が捕捉されることによりエラーページが表示されます。
一方でテスト環境 (RAILS_ENV = test
) では、デフォルトの設定のままではこれが無効になっており、エラーページは表示されません。
例外が発生することをテストすることができるため通常はこの動作は望ましいのですが、Requests Spec や Features Spec などで、エラー発生時の表示やステータスコードをテストしたい時があります。
そういう時のために、一時的にActionDispatch::ShowExceptions
での例外捕捉を有効にし、エラーページをレスポンスできるようにする方法を調べました。
*) exceptions_app
については別記事「Railsアプリの例外ハンドリングとエラーページの表示についてまとめてみた - Qiita」を参照してください
前提
- Rails 4.2.6
手っ取り早いコード
実行exampleの前後に下記のような処理を行えばOKです。
shared_context 'Show Exceptions', show_exceptions: true do
around(:each) do |example|
show_detailed_exceptions = Rails.application.env_config['action_dispatch.show_detailed_exceptions']
show_exceptions = Rails.application.env_config['action_dispatch.show_exceptions']
Rails.application.env_config['action_dispatch.show_detailed_exceptions'] = false
Rails.application.env_config['action_dispatch.show_exceptions'] = true
example.run
Rails.application.env_config['action_dispatch.show_detailed_exceptions'] = show_detailed_exceptions
Rails.application.env_config['action_dispatch.show_exceptions'] = show_exceptions
end
end
# `show_exceptions`メタを付けて実行する
describe 'Get /hoge', show_exceptions: true do
...
end
どうしてそうなるか、ざっくり説明していきます。
その説明の前提として、まずは Rack Middleware について少し説明します(知ってる人は飛ばしちゃってください)
Rack Middleware について
Railsデフォルトの Rack Middleware はRails::Application::DefaultMiddlewareStack
で定義されており、下記のrakeタスクでも確認できます。
$ bin/rake middleware
... (省略) ...
use ActionDispatch::ShowExceptions
use ActionDispatch::DebugExceptions
... (省略) ...
run MyApp::Application.routes
この定義によると、ActionDispatch::ShowExceptions
が外側に、ActionDispatch::DebugExceptions
が内側にスタックされています。
前者はexceptions_app
による例外捕捉の仕組みで、後者はデバッグ画面や例外のログ出力などのデバッグを目的とした仕組みです。
つまり内側のDebugExceptions
で捕捉されなかった例外が、外側のShowExceptions
で捕捉されます.
Rails Application から Rack Middleware が call される際、ハッシュenv
が渡されます。
このenv
にはRails::Application#env_config
があらかじめ結合されて、env_config
にはRails.application.config
で設定した値が割り当てられます。
(Rails.application.config
は、config/application.rb
やconfig/environments/development.rb
などで設定するやつです)
env_config
は Rack Middleware 起動時にセットされます。それ以降はRails.application.config
を設定しなおしてもenv_config
には反映されないので、テスト実行中に切り替えるにはenv_config
を直接設定し直す必要があります。
これらの前提をもとに、冒頭のコードに出てきたenv_config['action_dispatch.show_exceptions']
とenv_config['action_dispatch.show_detailed_exceptions']
について説明します。
エラーページ表示条件に関わるconfigたち
env_config['action_dispatch.show_exceptions']
ActionDispatch::ShowExceptions
とActionDispatch::DebugExceptions
が例外を捕捉するかどうかを決める値です。
元になっている設定はconfig.action_dispatch.show_exceptions
で、これはデフォルトtrueです。
true のとき
-
action_dispatch.show_detailed_exceptions
も true のとき、DebugExceptions
で例外を捕捉してデバッグ用エラーページを表示 -
action_dispatch.show_detailed_exceptions
が false なら、DebugExceptions
では例外を捕捉せず、外側のShowExceptions
で捕捉しエラーページを表示
false のとき
-
DebugExceptions
でもShowExceptions
でも例外を捕捉しない
env_config['action_dispatch.show_detailed_exceptions']
ActionDispatch::DebugExceptions
がデバッグ用エラーページを表示するかどうかを決める値です。
元になっている設定はconfig.consider_all_requests_local
で、名前が異なるのと、他の用途でも使われるので注意ですね。
action_dispatch.show_exceptions
が true のときに有効になりますので、以下はそれが前提の説明です。
true のとき
-
DebugExceptions
で捕捉した例外をもとに、デバッグ用エラーページを表示
false のとき
- そのまま例外を raise する。その例外はその後
ShowExceptions
で捕捉され、エラーページが表示
テスト環境では
rails generate
で生成されるデフォルトの設定 (config/environments/test.rb
) では、下記のようになっています。
config.consider_all_requests_local = true
config.action_dispatch.show_exceptions = false
だから例外が発生してもエラーページが表示されなかったんですね。
また、テスト実行中の設定の切り替えは、Rails.application.config
ではなくRails.application.env_config
でないと反映されません。
従って、example 実行前に
Rails.application.env_config['action_dispatch.show_detailed_exceptions'] = false
Rails.application.env_config['action_dispatch.show_exceptions'] = true
このような設定をしていたというわけです。