deviseの機能をRspecでテストする

# はじめに
既存Railsアプリにdeviseを導入した際に,
もともとあったRspecのテストを通すために苦労したので書いていきます。

---

#### バージョン
```
rails  : 6.1.1
ruby   : 2.6.6
devise : 4.7.3
```


<br>


### 原因
私がハマった原因は

 * deviseのテストヘルパーを使うための設定に関する記述が間違っていたこと
 * deviseのユーザー認証の仕組みをきちんと理解していなかったこと

です。

<br>

[公式README](https://github.com/heartcombo/devise)のTest helpersの欄にもある通り、
deviseではtestで使える簡単にログイン・ログアウトができるヘルパーがあります。
書いてあるとおり、使えるようにするには`spec/rails_helper.rb`に以下の記述をします。

```rb:spec/rails_helper.rb
RSpec.configure do |config|
  config.include Devise::Test::ControllerHelpers, type: :controller
  config.include Devise::Test::ControllerHelpers, type: :view
end
```
使い方はこんな感じ

```rb:spec/requests/users_spec.rb
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`に以下の記述を追加します。

```rb: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
```

これでまずヘルパーがちゃんと使えるようになりました。
が、私の場合はこれだけではまだダメでした。
<br>
失敗したテストの内容とエラーをみてみます。

* テスト 

```rb
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の設定はこうでした。

```rb:spec/factories/users.rb
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`カラムに関する記述がありません、これに書き加えます。


```rb:spec/factories/users.rb
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などを読み込んでおく事が重要だと身にしみました。
<br>
以上です。ありがとうございました。

---

#### 参考記事
[How To: sign in and out a user in Request type specs](https://github.com/heartcombo/devise/wiki/How-To:-sign-in-and-out-a-user-in-Request-type-specs-(specs-tagged-with-type:-:request))
[devise](https://github.com/heartcombo/devise)

---