Help us understand the problem. What is going on with this article?

Rails の exceptions_app によるエラーページの表示をテストする

More than 3 years have passed since last update.

背景

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.rbconfig/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::ShowExceptionsActionDispatch::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

このような設定をしていたというわけです。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした