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

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

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

## 前提条件
- Ruby 2.6.5
- Rails 5.2.4

## テーブル構成
全ては記述してませんが、テーブル構成はこんな感じです。
[Pundit](https://github.com/varvet/pundit)というgemを使い、以下のとおりアクセス制限をしました。
・ `material`の`status`が`learning`の時は、materialの編集画面（materials/:id/edit）に、アクセスできる設定
・ `material`の`status`が`complete`の時は、materialの編集画面（materials/:id/edit）に、アクセスできない設定

```Ruby: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.まず、[こちら](https://qiita.com/Hyuga-Tsukui/items/57fa77df2b2942414307)の記事を参考に、下記のとおり記述しました。

#### FactoryBotを記述
```Ruby:spec/factories/users.rb
FactoryBot.define do
  factory :user do
    id { 1 }
    name { 'sample' }
    email { 'sample@example.com' }
  end
end
```

```Ruby:spec/factories/materials.rb
FactoryBot.define do
  factory :material do
    id { 1 }
    title { 'test1' }
    status { 'learning' }
  end
end
```

#### deviseのhelperメソッドを呼び出せるように設定
```Ruby:spec/rails_helper.rb
RSpec.configure do |config|
  # 下記コードを追加しました
  config.include Devise::Test::IntegrationHelpers, type: :request
end
```

#### 下記のとおりRequestSpecを記述
```Ruby: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メソッドを使用しました。
下記が最終的なコードです。テストも成功しました。

```Ruby: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
- https://qiita.com/Hyuga-Tsukui/items/57fa77df2b2942414307
- https://qiita.com/t2kojima/items/ad7a8ade9e7a99fb4384
- https://qiita.com/zongxiaojie/items/d488edd42ba3864859c7
- https://zenn.dev/kitabatake/articles/start-to-like-the-devise