タイトルのとおりですが、deviseのメール認証機能を使用したアプリで、RequestSpecでテストを行った際にハマりました
概要
- Railsでdeviseのメール認証機能を使用したアプリを作成しました。
- 特定の値を持つインスタンスは、特定のページに遷移できない処理を行いたかったので、Punditというgemを使いました。
- 2のテストをするため、RequestSpecでテストコードを記述しましたが、期待した結果が出力されず、ハマりました
前提条件
- Ruby 2.6.5
- Rails 5.2.4
テーブル構成
全ては記述してませんが、テーブル構成はこんな感じです。
Punditというgemを使い、以下のとおりアクセス制限をしました。
・ material
のstatus
がlearning
の時は、materialの編集画面(materials/:id/edit)に、アクセスできる設定
・ material
のstatus
がcomplete
の時は、materialの編集画面(materials/:id/edit)に、アクセスできない設定
db/schema.rb
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.string "email", default: "", null: false
end
create_table "materials", force: :cascade do |t|
t.string "title", null: false
t.bigint "user_id"
# material_modelでenum status: { learning: 0, complete: 1 }と定義してます。
t.integer "status", default: 0, null: false
end
実行したこと
1.まず、こちらの記事を参考に、下記のとおり記述しました。
FactoryBotを記述
spec/factories/users.rb
FactoryBot.define do
factory :user do
id { 1 }
name { 'sample' }
email { 'sample@example.com' }
end
end
spec/factories/materials.rb
FactoryBot.define do
factory :material do
id { 1 }
title { 'test1' }
status { 'learning' }
end
end
deviseのhelperメソッドを呼び出せるように設定
spec/rails_helper.rb
RSpec.configure do |config|
# 下記コードを追加しました
config.include Devise::Test::IntegrationHelpers, type: :request
end
下記のとおりRequestSpecを記述
spec/request/materials_spec.rb
require 'rails_helper'
RSpec.describe 'Materials', type: :request do
let(:user) { FactoryBot.create(:user) }
let(:learning_material) { FactoryBot.create(:material, user: user) }
let(:complete_material) { FactoryBot.create(:material, id: 2, status: 'complete', user: user) }
describe 'GET /materials/:id/edit' do
before do
sign_in(user) # deviseのsign_inメソッド利用し、ログイン処理をする
end
it 'materialがlearningの場合、リクエストは成功する' do
get edit_material_path(learning_material)
expect(response.status).to eq 200
end
end
end
2.spec/request/materials_spec.rb
を実行!しかし、想定の結果にならず・・・
materials_spec.rb
の結果、response.status
は302
でした。
302
は、リダイレクトされた際のHTTPステータスコードです。
実際にアプリを動かした際には、成功しているのになぜだろう??
3.原因の調査
binding.irb
でテストを止めて、response
の中身を見てみました。
その結果、下記の気になる情報が見つかりました。
@buf=["<html><body>You are being <a href=\"http://www.example.com/users/sign_in\">redirected</a>.</body></html>"]
{"alert"=>"メールアドレスの本人確認が必要です。"}
- users/sign_inはlogin画面のURL。login画面にredirectされてる。
- メール認証無しでloginしようとした際のアラートが表示されている。
アカウント作成後のメール認証ができていない!!!ということが発覚しました。
4.対応
userアカウント作成後のメール認証を行うために、deviseのconfirmメソッドを使用しました。
下記が最終的なコードです。テストも成功しました。
spec/request/materials_spec.rb
require 'rails_helper'
RSpec.describe 'Materials', type: :request do
let(:user) { FactoryBot.create(:user) }
let(:learning_material) { FactoryBot.create(:material, user: user) }
let(:complete_material) { FactoryBot.create(:material, id: 2, status: 'complete', user: user) }
describe 'GET /materials/:id/edit' do
before do
user.confirm # devise:メール認証機能(サインインの前に認証必要)
sign_in(user) # deviseのsign_inメソッド利用し、ログイン処理をする
end
it 'materialがlearningの場合、リクエストは成功する' do
get edit_material_path(learning_material)
expect(response.status).to eq 200
end
end
end