【RSpec】Railsチュートリアル第6版　第12章

#はじめに
Railsチュートリアル第6版のテストをRSpecで書き直していく。

###目次
- [第3章](https://qiita.com/supyolo888/items/35081ead2cfafb40a995)
- [第4章](https://qiita.com/supyolo888/items/939b670b62eb18467f6d)
- [第5章](https://qiita.com/supyolo888/items/42234ac2ed3919b14586)
- [第6章](https://qiita.com/supyolo888/items/bd10e64c77fe391cf203)
- [第7章](https://qiita.com/supyolo888/items/d2824ced26402b5f58d1)
- [第8章](https://qiita.com/supyolo888/items/508caaa40edf307093ed)
- [第9章](https://qiita.com/supyolo888/items/62e471b07a0c9a81575d)
- [第10章](https://qiita.com/supyolo888/items/a8695b20ec8082f85223)
- [第11章](https://qiita.com/supyolo888/items/5c81331e46f1d9d5ab3c)
- 第12章（本記事）
- [第13章](https://qiita.com/supyolo888/items/c88b6acd4ff55750defc)
- [第14章](https://qiita.com/supyolo888/items/532390902bf3e0f5348e)

#Minitest

###パスワード再設定のプレビューメソッド（完成）

```rb:test/mailers/previews/user_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/account_activation
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
  end

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/password_reset
  def password_reset
    user = User.first
    user.reset_token = User.new_token
    UserMailer.password_reset(user)
  end
end
```

###パスワード再設定用メイラーメソッドのテストを追加する

```rb:test/mailers/user_mailer_test.rb
require 'test_helper'

class UserMailerTest < ActionMailer::TestCase

  test "account_activation" do
    user = users(:michael)
    user.activation_token = User.new_token
    mail = UserMailer.account_activation(user)
    assert_equal "Account activation", mail.subject
    assert_equal [user.email], mail.to
    assert_equal ["noreply@example.com"], mail.from
    assert_match user.name,               mail.body.encoded
    assert_match user.activation_token,   mail.body.encoded
    assert_match CGI.escape(user.email),  mail.body.encoded
  end

  test "password_reset" do
    user = users(:michael)
    user.reset_token = User.new_token
    mail = UserMailer.password_reset(user)
    assert_equal "Password reset", mail.subject
    assert_equal [user.email], mail.to
    assert_equal ["noreply@example.com"], mail.from
    assert_match user.reset_token,        mail.body.encoded
    assert_match CGI.escape(user.email),  mail.body.encoded
  end
end
```

###パスワード再設定の統合テスト

```rb:test/integration/password_resets_test.rb
require 'test_helper'

class PasswordResetsTest < ActionDispatch::IntegrationTest

  def setup
    ActionMailer::Base.deliveries.clear
    @user = users(:michael)
  end

  test "password resets" do
    get new_password_reset_path
    assert_template 'password_resets/new'
    assert_select 'input[name=?]', 'password_reset[email]'
    # メールアドレスが無効
    post password_resets_path, params: { password_reset: { email: "" } }
    assert_not flash.empty?
    assert_template 'password_resets/new'
    # メールアドレスが有効
    post password_resets_path,
         params: { password_reset: { email: @user.email } }
    assert_not_equal @user.reset_digest, @user.reload.reset_digest
    assert_equal 1, ActionMailer::Base.deliveries.size
    assert_not flash.empty?
    assert_redirected_to root_url
    # パスワード再設定フォームのテスト
    user = assigns(:user)
    # メールアドレスが無効
    get edit_password_reset_path(user.reset_token, email: "")
    assert_redirected_to root_url
    # 無効なユーザー
    user.toggle!(:activated)
    get edit_password_reset_path(user.reset_token, email: user.email)
    assert_redirected_to root_url
    user.toggle!(:activated)
    # メールアドレスが有効で、トークンが無効
    get edit_password_reset_path('wrong token', email: user.email)
    assert_redirected_to root_url
    # メールアドレスもトークンも有効
    get edit_password_reset_path(user.reset_token, email: user.email)
    assert_template 'password_resets/edit'
    assert_select "input[name=email][type=hidden][value=?]", user.email
    # 無効なパスワードとパスワード確認
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "barquux" } }
    assert_select 'div#error_explanation'
    # パスワードが空
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "",
                            password_confirmation: "" } }
    assert_select 'div#error_explanation'
    # 有効なパスワードとパスワード確認
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "foobaz" } }
    assert is_logged_in?
    assert_not flash.empty?
    assert_redirected_to user
  end
end
```

#RSpec

###パスワード再設定のプレビューメソッド（完成）

```rb:spec/mailers/previews/user_mailer_preview.rb
# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
class UserMailerPreview < ActionMailer::Preview

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/account_activation
  def account_activation
    user = User.first
    user.activation_token = User.new_token
    UserMailer.account_activation(user)
  end

  # Preview this email at
  # http://localhost:3000/rails/mailers/user_mailer/password_reset
  def password_reset
    user = User.first
    user.reset_token = User.new_token
    UserMailer.password_reset(user)
  end
end
```

###パスワード再設定用メイラーメソッドのテストを追加する

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

RSpec.describe UserMailer, type: :mailer do

  describe "account_activation" do

    let(:user) { FactoryBot.create(:user) }
    let(:mail) { UserMailer.account_activation(user) }
    
    it "renders the headers" do
      expect(mail.subject).to eq("Account activation")
      expect(mail.to).to eq([user.email])
      expect(mail.from).to eq(["noreply@example.com"])
    end

    it "renders the body" do
      expect(mail.body.encoded).to match(user.name)
      expect(mail.body.encoded).to match(user.activation_token)
      expect(mail.body.encoded).to match(CGI.escape(user.email))
    end

  end

  describe "password_reset" do

    let(:user) { FactoryBot.create(:user) }
    let(:mail) { UserMailer.password_reset(user) }

    before do
      user.reset_token = User.new_token
    end

    it "renders the headers" do
      expect(mail.subject).to eq("Password reset")
      expect(mail.to).to eq([user.email])
      expect(mail.from).to eq(["noreply@example.com"])
    end
    it "renders the body" do
      expect(mail.body.encoded).to match(user.reset_token)
      expect(mail.body.encoded).to match(CGI.escape(user.email))
    end

  end

end
```

*"account_activation"*のときと違い、user.reset_tokenが自動作成されないので、*"renders the headers"*と*"renders the body"*の両方でuser.reset_tokenを使用する為に`before do`で作成する。

###パスワード再設定の統合テスト

```rb:spec/requests/password_resets_spec.rb
require 'rails_helper'

RSpec.describe "PasswordResets", type: :request do
  
  let(:user) { FactoryBot.create(:user) }

  before do
    ActionMailer::Base.deliveries.clear
  end


  it "password resets" do
    get new_password_reset_path
    expect(response).to have_http_status(:success)
    assert_select "input[name=?]", "password_reset[email]"
    post password_resets_path, params: { password_reset: { email: "" } }
    expect(flash[:danger]).to eq "Email address not found"
    expect(response).to have_http_status(:success)
    post password_resets_path,
         params: { password_reset: { email: user.email } }
    expect(user.reset_digest).not_to eq user.reload.reset_digest
    expect(ActionMailer::Base.deliveries.size).to eq 1
    expect(flash[:info]).to eq "Email sent with password reset instructions"
    expect(response).to redirect_to root_url
    user.create_reset_digest
    get edit_password_reset_path(user.reset_token, email: "")
    expect(response).to redirect_to root_url
    user.toggle!(:activated)
    get edit_password_reset_path(user.reset_token, email: user.email)
    expect(response).to redirect_to root_url
    user.toggle!(:activated)
    get edit_password_reset_path("wrong token", email: user.email)
    expect(response).to redirect_to root_url
    get edit_password_reset_path(user.reset_token, email: user.email)
    expect(response).to have_http_status(:success)
    assert_select "input[name=email][type=hidden][value=?]", user.email
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "barquux" } }
    assert_select "div#error_explanation"
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "",
                            password_confirmation: "" } }
    assert_select "div#error_explanation"
    patch password_reset_path(user.reset_token),
          params: { email: user.email,
                    user: { password:              "foobaz",
                            password_confirmation: "foobaz" } }
    expect(is_logged_in?).to be_truthy
    expect(flash[:success]).to eq "Password has been reset."
    expect(response).to redirect_to user_path(user)
  end

end
```

request specに記述していく。
`user.create_reset_digest`でパスワード再設定の属性を設定する。
`assert_template`の部分が思いつかず`expect(response).to have_http_status(:success)`で代用しているが、改善の余地あり。