Rails Tutorialの第４版をRSpecでテスト-6

[Ruby on Rails チュートリアル][link-1]の第4版を学習し、[Everyday Rails - RSpecによるRailsテスト入門][link-2]でRSpecを学習したので、[Ruby on Rails チュートリアル][link-1]をRSpecでテストしてみました。
今回は第12章のテストを書きます。

第3章～第11章のテストは以下。
 - [Railsチュートリアルの第4版をRSpecでテスト-1][link-3]（第3章～第6章）
 - [Railsチュートリアルの第4版をRSpecでテスト-2][link-4]（第7章～第8章）
 - [Railsチュートリアルの第4版をRSpecでテスト-3][link-5]（第9章）
 - [Railsチュートリアルの第4版をRSpecでテスト-4][link-6]（第10章）
 - [Railsチュートリアルの第4版をRSpecでテスト-5][link-8]（第11章）

[link-1]:https://railstutorial.jp/
[link-2]:https://leanpub.com/everydayrailsrspec-jp
[link-3]:https://qiita.com/yano12/items/eba8693272c31d6af7f0
[link-4]:https://qiita.com/yano12/items/4ef93129a50249adcb8d
[link-5]:https://qiita.com/yano12/items/319615e6e5fdcd7d844a
[link-6]:https://qiita.com/yano12/items/99e3fd4213f61339bad4
[link-7]:https://railstutorial.jp/chapters/account_activation?version=5.1#sec-exercises_mailer_templates
[link-8]:https://qiita.com/yano12/items/f5167416f3c20e1896d4
[link-9]:https://railstutorial.jp/chapters/password_reset?version=5.1#cha-password_reset

# パスワードの再設定
[Ruby on Rails チュートリアル 第12章][link-9]のパスワードの再設定をテストしていきます。

## 送信メールのテスト
パスワードの再設定メールの送信とプレビューのテストをspec/mailers/user_mailer_spec.rbに追加します。

```spec/mailers/user_mailer_spec.rb
require "rails_helper"

RSpec.describe UserMailer, type: :mailer do
  let(:user) { FactoryBot.create(:user) }
  
  describe "account_activation" do
    # アカウント有効化メールのテストは省略
  end

  describe "password_reset" do
    let(:mail) { UserMailer.password_reset(user) }

    # メール送信のテスト
    it "renders the headers" do
      user.reset_token = User.new_token                        # この行がないとエラーになった
      expect(mail.to).to eq ["tester3@example.com"]
      expect(mail.from).to eq ["noreply@example.com"]
      expect(mail.subject).to eq "Password reset"
    end

    # メールプレビューのテスト
    it "renders the body" do
      user.reset_token = User.new_token                        # この行がないとエラーになった
      expect(mail.body.encoded).to match user.reset_token
      expect(mail.body.encoded).to match CGI.escape(user.email)
    end
  end
end
```

基本的にはアカウント有効化のテストと同じですが，各テストの最初でuser.reset_tokenを定義しないとエラーになってしまいます。
実行しテストがパスするのを確認します。

## パスワードの再設定をテストする
続いてパスワードの再設定のテストをリクエストスペックに書いていきます。
テストの手順は次の通りです。
最初に「forgot password」フォームを表示して無効なメールアドレスを送信し、次はそのフォームで有効なメールアドレスを送信します。
後者ではパスワード再設定用トークンが作成され、再設定用メールが送信されます。
続いて、GETリクエストでメールのリンクを開いて無効な情報を送信し、次にそのリンクから有効な情報を送信して、それぞれが期待どおりに動作することを確認します。

```spec/requests/password_reset_spec.rb
require 'rails_helper'

RSpec.describe "User pages", type: :request do
  let(:user) { FactoryBot.create(:user) }
  
  include ActiveJob::TestHelper
    
  it "resets password" do
    perform_enqueued_jobs do
      # メールアドレスが無効
      post password_resets_path, params: { password_reset: { email: "" } }
      expect(response).to render_template(:new)
      # メールアドレスが有効
      post password_resets_path, params: { password_reset: { email: user.email }}
      expect(response).to redirect_to root_path
      
      # パスワード再設定フォームのテスト
      user = assigns(:user)
      # メールアドレスが無効
      get edit_password_reset_path(user.reset_token, email: "" )
      expect(response).to redirect_to root_path
      # メールアドレスが有効で、トークンが無効
      get edit_password_reset_path('wrong token', email: user.email )
      expect(response).to redirect_to root_path
      # メールアドレスもトークンも有効
      get edit_password_reset_path(user.reset_token, email: user.email )
      expect(response).to render_template(:edit)
      # 無効なパスワードとパスワード確認
      patch password_reset_path(user.reset_token), 
          params: { email: user.email,
                    user: { password: "foobaz",
                            password_confirmation: "barquux" } }
      expect(response).to render_template(:edit)
      # パスワードが空
      patch password_reset_path(user.reset_token), 
          params: { email: user.email,
                    user: { password: "",
                            password_confirmation: "" } }
      expect(response).to render_template(:edit)
      # 有効なパスワードとパスワード確認
      patch password_reset_path(user.reset_token), 
          params: { email: user.email,
                    user: { password: "foobaz",
                            password_confirmation: "foobaz" } }
      expect(session[:user_id]).to eq user.id
      expect(response).to redirect_to user_path(user)
    end
  end
end
```

実行しテストがパスするのを確認します。

以上でパスワード再設定のテストは終わりです。