きっかけ
ActiveRecord::RecordNotFound
を発生させるテストケースがなぜか失敗し、沼ってしまいました。
原因がわからなくて気持ち悪かったので色々と調査してみました。
やりたいテスト
以下のように、staffsテーブルから対象のstaff_id
を持つレコードを取得するアクションを考えます。
find
メソッドを使用しているため、テーブルにレコードが存在しない場合はActiveRecord::RecordNotFound
が発生します。
class ProfilesController
# get profiles_show_path(:person_id)で実行されるアクション
def show
@staff = Staff.find(params[:person_id])
end
end
この時、例外が発生していることをテストするケースを考えます。
assert_raise
を使用して以下のように書いてみました。
test '対象のstaffレコードが取得できない場合に例外が発生する' do
assert_raise(ActiveRecord::RecordNotFound) do
# staff_id = 100のレコードは存在しないため例外が発生する
get profiles_show_path(100)
end
end
期待通りActiveRecord::RecordNotFound
が発生した場合にこのテストはパスします。
ですが...
# => Minitest::Assertion: ActiveRecord::RecordNotFound expected but nothing was raised.
は?例外発生してないんだが...
私の心の中
「find
ってレコード取得できない時に例外発生すると思ってたんだけど違ったっけ」
→ あってます。新人の頃200回くらいググりましたよね。
「どこかにrescue
処理書いたっけ、探してみよう」
→ 見つかりません。だって書いてないもん。
もうパニックです。
犯人
この挙動が理解できず、色々と調べていくうちに犯人がわかりました。
test環境のconfigファイルを見てみる
config/environments/test.rb
の中に以下のような記述があります。
# Render exception templates for rescuable exceptions and raise for other exceptions.
config.action_dispatch.show_exceptions = :rescuable
こいつの説明はRailsガイドにありました。
config.action_dispatch.show_exceptions設定は、リクエストへの応答中に発生した例外をAction Packで処理する方法を制御します。
:rescuable: config.action_dispatch.rescue_responsesで宣言されている例外についてはHTMLエラーページを表示する
Rails 7.1以降で生成したアプリケーションのconfig/environments/test.rbには config.action_dispatch.show_exceptions = :rescuableが設定されます。
なんと、テスト環境では発生したActiveRecord::RecordNotFound
がレスキューされ、404ページをレンダーするように処理されていたのです...
テストを書き直す
以上を踏まえて以下のようにテストを書き直しました。
test '対象のstaffレコードが取得できない場合に例外が発生する' do
get profiles_show_path(100)
assert_response(404)
end
補足
config.action_dispatch.show_exceptions = :rescuable
がデフォルト設定されるのはRails7.1以降です。:rescuable
以外には:all
と:none
が選択肢としてあります。
それ以前のバージョンではtrue
とfalse
が選択肢だったようです。
しかもconfig.action_dispatch.show_exceptions = false
がデフォルト設定されているので普通に例外発生します。知らんかった...
最後に
この記事が私みたいに沼る人を救えるよう願います。