RSpecテスト内でログイン出来なくてハマった話
発生した問題
各要求に対して適切なHTTPステータスコードが正確に返されることを検証するテストを実行していたところ、ログインに関する問題が発生し、この問題によってテストが失敗する状況に直面しました。
該当コード
require 'rails_helper'
RSpec.describe 'Postsコントローラーのテスト', type: :request do
let(:user) { create(:user) }
describe 'ログイン済み' do
before do
sign_in user
end
context 'GET #index' do
it '200 OK' do
get root_path
expect(response.status).to eq 200
end
end
end
# ...
end
FactoryBot.define do
factory :user do
name { Faker::Lorem.characters(number: 10) }
email { Faker::Internet.email }
password { 'password' }
password_confirmation { 'password' }
agreement { true }
end
end
# ...
config.include FactoryBot::Syntax::Methods
config.include Devise::Test::IntegrationHelpers, type: :request
end
問題発生
上記のテストコードを実行した際に、次のようなエラーが発生し、テストが失敗しました。
/myapp-backend# bundle exec rspec spec/requests
Failures:
1) Postsコントローラーのテスト ログイン済み GET #index 200 OK
Failure/Error: expect(response.status).to eq 200
expected: 200
got: 302
(compared using ==)
# ./spec/requests/posts_request_spec.rb:14:in `block (4 levels) in <top (required)>'
このエラーについて簡潔に説明すると、root_path
自体は正しく見つかったものの(302 Found)
、実際にそのパスへ遷移できていないという状況を示しています。
試したこと
まず、puts
を使用して具体的なエラー内容を確認します。
# ...
context 'GET #index' do
it '200 OK' do
get root_path
puts "Response location: #{response.location}"
expect(response.status).to eq 200
end
end
# ...
Response location: http://www.example.com/users/sign_in
エラー内容から見ると、ログインが失敗して画面が切り替わっていないようです。
次に、ログイン失敗の原因を調査するために、Devise::Test::IntegrationHelpers
から渡されるsign_in
メソッドの内部動作を調べてみます。
ということでmanager.rb
のargs
を調べた結果、
{:scope=>:user, :message=>:unconfirmed, :action=>"unauthenticated", :attempted_path=>"/"}
問題の原因がuser
がunconfirmed
状態であることが判明しました。
unconfirmed
状態とは、ユーザーがまだ確認メールを受信してアカウントを有効化していない状態を指します。この状態では、ログインが許可されていないため、テストが失敗しているようです。
解決
ユーザーのメール認証を完了させれば良いです。
以下に自分の知っている二通りの方法を示します。
方法1
# ...
let(:user) { create(:user) }
describe 'ログイン済み' do
before do
+ user.confirm
sign_in user
end
# ...
上のようにuser.confirm
を加えることでユーザーのメール認証をスキップすることができます。
方法2
FactoryBot.define do
factory :user do
name { Faker::Lorem.characters(number: 10) }
email { Faker::Internet.email }
password { 'password' }
password_confirmation { 'password' }
agreement { true }
+ confirmed_at { Time.now }
end
end
もしくはユーザーの作成時に直接confirmed_at
を指定してあげれば良いです。Devise
のメール認証機能はconfirmed_at
カラムがnil
かどうかで判定しているので、適当な日時で埋めてあげればメール認証完了になります。
終わりに
このエラー解消に半日くらいかかりました。ログイン関係のエラーは難しいです。
Discussion