search
LoginSignup
1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

deviseのメール認証機能を利用している場合のRequestSpecでハマった話

タイトルのとおりですが、deviseのメール認証機能を使用したアプリで、RequestSpecでテストを行った際にハマりました

概要

  1. Railsでdeviseのメール認証機能を使用したアプリを作成しました。
  2. 特定の値を持つインスタンスは、特定のページに遷移できない処理を行いたかったので、Punditというgemを使いました。
  3. 2のテストをするため、RequestSpecでテストコードを記述しましたが、期待した結果が出力されず、ハマりました

前提条件

  • Ruby 2.6.5
  • Rails 5.2.4

テーブル構成

全ては記述してませんが、テーブル構成はこんな感じです。
Punditというgemを使い、以下のとおりアクセス制限をしました。
materialstatuslearningの時は、materialの編集画面(materials/:id/edit)に、アクセスできる設定
materialstatuscompleteの時は、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.status302でした。
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

参考URL

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1
Help us understand the problem. What are the problem?