はじめに
既存Railsアプリにdeviseを導入した際に,
もともとあったRspecのテストを通すために苦労したので書いていきます。
バージョン
rails : 6.1.1
ruby : 2.6.6
devise : 4.7.3
原因
私がハマった原因は
- deviseのテストヘルパーを使うための設定に関する記述が間違っていたこと
- deviseのユーザー認証の仕組みをきちんと理解していなかったこと
です。
公式READMEのTest helpersの欄にもある通り、
deviseではtestで使える簡単にログイン・ログアウトができるヘルパーがあります。
書いてあるとおり、使えるようにするにはspec/rails_helper.rb
に以下の記述をします。
RSpec.configure do |config|
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
end
使い方はこんな感じ
require 'rails_helper'
RSpec.describe "Users" do
it "ログインしてる時、ユーザーの詳細ページにアクセスできる" do
user = create(:user)
sign_in user
get users_path
expect(response).to have_http_status(200)
end
end
私はこれでtestのどこでもヘルパーが使えるようになると思っていたので、
何回やっても失敗するテストをみて首をかしげていました。
解決手順①
このヘルパーを上記のようなrequestテストでも本当に使えるようにはspec/rails_helper.rb
に以下の記述を追加します。
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers, type: :request #追加
config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::ControllerHelpers, type: :view
end
これでまずヘルパーがちゃんと使えるようになりました。
が、私の場合はこれだけではまだダメでした。
失敗したテストの内容とエラーをみてみます。
- テスト
require 'rails_helper'
RSpec.describe "Users" do
let! (:user) { create(:user) }
let! (:user2) { create(:user) }
describe '#edit' do
context '本人ではない場合' do
befor do
sign_in user2
get edit_user_path user
end
it 'Homeにリダイレクトされる' do
expect(response).to redirect_to root_path
end
end
end
end
- エラー
Failure/Error: expect(response).to redirect_to root_path
Expected response to be a redirect to <http://www.example.com/> but was a redirect to <http://www.example.com/users/sign_in>.
Expected "http://www.example.com/" to be === "http://www.example.com/users/sign_in".
本人ではないユーザーがユーザー情報編集ページにアクセスすると、
Homeにリダイレクトされるということを確認するテストです。
エラーは、期待していたhttp://www.example.com/
ではなく、
http://www.example.com/users/sign_in
にリダイレクトされていますよ、と伝えています。
何故か。ログインページにリダイレクトされています。
解決手順②
原因はFactorybotの設定にありました。
今回はdeviseのconfirmable
という
「ユーザー登録時に認証メールを送信し、メールに記載されているリンクにアクセスすることで本登録完了」
といった感じのよくある機能を提供してくれるモジュールを使用していました。
この機能は、ユーザーがメールのリンクにアクセスした時confirmed_at
というカラムに認証の日時を保存し、その有無で機能へのアクセスを制限するというものです。
テストが通らなかった時のFactorybotの設定はこうでした。
FactoryBot.define do
factory :user do
name { "test_user" }
sequence(:email) { |n| "test#{n}@exmple.com" }
password { "password" }
password_confirmation { "password" }
end
end
confirmed_at
カラムに関する記述がありません、これに書き加えます。
FactoryBot.define do
factory :user do
name { "test_user" }
sequence(:email) { |n| "test#{n}@exmple.com" }
password { "password" }
password_confirmation { "password" }
confirmed_at { Date.today } ##追加
end
end
これでテストが通るようになりました。
deviseに対する理解が全く足りていませんでした。
当たり前のことですが、使う前にきちんとREADMEなどを読み込んでおく事が重要だと身にしみました。
以上です。ありがとうございました。
参考記事
How To: sign in and out a user in Request type specs
devise