OmniAuthを利用したログインのsystem specで、ログインに失敗した場合のテストが次のようなエラーになった。
1) ユーザがログインする ログインに失敗したとき "ログインに失敗しました"と表示されること Failure/Error: click_link 'Twitterでログイン' OmniAuth::Error: invalid_credentials # ./spec/system/login_spec.rb:98:in `block (3 levels) in <top (required)>'
もとになったspecはこちら。
context 'ログインに失敗したとき' do around do |example| original_mock_auth = OmniAuth.config.mock_auth[:twitter] OmniAuth.config.mock_auth[:twitter] = :invalid_credentials visit root_path click_link 'Twitterでログイン' example.run OmniAuth.config.mock_auth[:twitter] = original_mock_auth end it '"ログインに失敗しました"と表示されること' do expect(page).to have_content 'ログインに失敗しました' end end
これまでは普通にテストが通っていたのでなんだろうと思って調べた。
原因
- OmniAuthはデフォルトの挙動して、RACK_ENVがdevelopmentのときに単にOmniAuth::Errorを投げる設定になっている
- omniauth/failure_endpoint.rb at 8179ba796aae82f857f63b50ae848a3fbe369b4d · omniauth/omniauth
- それ以外の環境ではエラー用のURLにリダイレクトする(これが期待していた挙動)
- jsを利用したsystem specのときにpumaをサーバとして使っている
- pumaは起動時にRACK_ENVが設定されていないと、developmentを設定する
- puma/server.rb at master · puma/puma
つまりテストの実行順序の問題で、pumaの起動がOmniAuthのテストより前に来てしまうとRACK_ENVがdevelopmentになり期待していた挙動と異なる振る舞いをするようになってしまっていた。
とりあえず明示的に、次の行をconfig/rails_helper.rb
に追加したところうまく動くようになった。
ENV['RACK_ENV'] ||= 'test'
所感
これ、自分と同じようにハマる人が出てくると思うのでライブラリ側でどうにかしたいのだけど、どのライブラリにどのようにPR投げるか悩ましいですね…
- rspec-rails
- puma
- capybara
- omniauth
のどれか。rspec-railsでrails_helper.rbのテンプレートをいじってRACK_ENVもRAILS_ENVと同様に設定するようにする、というのがいいだろうか。
おまけ
どこでRACK_ENVが設定されているか調べるのに使った雑なスクリプトを置いておきます
module DebugEnv def []=(*args) key = args.first if key == 'RAILS_ENV' || key == 'RACK_ENV' p args p caller end super end end ENV.singleton_class.prepend DebugEnv
あと、この手の環境変数関連のデバッグではspringは切っておいたほうが良さそう(ちゃんと調べきれてないけど、設定されていないはずのタイミングでRACK_ENVが設定されていて、これでかなり時間を奪われた><)
また、rake spec
や rake
でテストを実行しているときにはこの問題は発生しなさそう。