better_errorsは、Railsのデフォルトエラーページよりもエラーを詳細に表示し、デバッグを助けてくれるgemです。
READMEにある通り、基本的にはdevelopment環境での使用が想定されています。
しかしながら、今回、test環境で有効にしていたことによりハマったことがあったので、記録として残しておきます。
何が起きたか
テストを書いていたら、以下の状況になりました。
「例外を起こすことのテストで、RSpecで実際に例外が発生しているはずなのにraise_errorするとテストが失敗するぞ?」
↓実際に書いたテストを簡略化したもの
it do expect { get root_path }.to raise_error RuntimeError # root_pathにアクセスすると例外が発生するようにしているので、それを確認するテスト end
↓結果
Failure/Error: expect { get root_path }.to raise_error RuntimeError
expected RuntimeError but nothing was raised
画面上でアクセスしても例外が発生するのに、テストすると「例外なんて発生していないよ」と言われ、結構混乱しました。
原因: better_errorsがテスト環境にインストールされていた
色々試行錯誤する中で、悪さをしているライブラリがないかとGemfileを見ると、あることに気づきました。
group :development, :test do gem 'better_errors' end
あれ、better_errorsってテスト環境で有効にしなくても良いものでは?と思い、とりあえず消してみました。
↓修正後のGemfile
group :development do gem 'better_errors' end
結果、、、
Finished in 0.02689 seconds (files took 1.18 seconds to load) 1 example, 0 failures
これかー!
better_errorsをtest環境で有効にしてしまっていたのが原因とわかりました。
では、具体的に、なぜbetter_errorsをテスト環境に入れていると例外が捕捉できないのでしょうか?
better_errorsは例外をrescueする
better_errorsのコードを読んだら、例外をrescueして、カスタマイズしたエラー画面を表示していました。
そして例外はそのまま伝播させず、500のレスポンスとして返すようにしていました。
better_errors/middleware.rb
def protected_app_call(env) @app.call env rescue Exception => ex @error_page = @handler.new ex, env log_exception show_error_page(env, ex) end def show_error_page(env, exception=nil) # ...(略) [status_code, headers, [content]] end
だからRSpec側でraise_error XXXしても、例外は発生していない、と言われたんですね。
さいごに
しばらく、better_errorsというところに当たりをつけられず解決まで少し時間を要しましたが、原因がわかってスッキリしました。
このリポジトリにbetter_errorsを導入したのは私ではないのですが、おそらくsystem specの失敗時のスクリーンショットからデバッグをしやすくするために入れたのかなーと推測しています。
実はこれ以外にもbetter_errorsによって想定と違う動きをした、という例があるのでまた記事にして残しておきたいと思います。