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

#はじめに
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章（本記事）
- [第12章](https://qiita.com/supyolo888/items/fbb95671d39dfbf9df90)
- [第13章](https://qiita.com/supyolo888/items/c88b6acd4ff55750defc)
- [第14章](https://qiita.com/supyolo888/items/532390902bf3e0f5348e)

#Minitest

###fixtureのユーザーを有効にしておく

```test/fixtures/users.yml
michael:
  name: Michael Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  admin: true
  activated: true
  activated_at: <%= Time.zone.now %>

archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

lana:
  name: Lana Kane
  email: hands@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

malory:
  name: Malory Archer
  email: boss@example.gov
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>

<% 30.times do |n| %>
user_<%= n %>:
  name:  <%= "User #{n}" %>
  email: <%= "user-#{n}@example.com" %>
  password_digest: <%= User.digest('password') %>
  activated: true
  activated_at: <%= Time.zone.now %>
<% end %>
```

###アカウント有効化のプレビューメソッド

```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
    UserMailer.password_reset
  end
end
```

###Userメイラーのテスト

```rb:test/mailers/user_mailer_test.rb
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
end
```

###Userテスト内の抽象化したauthenticated?メソッド
 ```rb:test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar")
  end
  .
  .
  .
  test "authenticated? should return false for a user with nil digest" do
    assert_not @user.authenticated?(:remember, '')
  end
end
```

###ユーザー登録のテストにアカウント有効化を追加する

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

class UsersSignupTest < ActionDispatch::IntegrationTest

  def setup
    ActionMailer::Base.deliveries.clear
  end

  test "invalid signup information" do
    get signup_path
    assert_no_difference 'User.count' do
      post users_path, params: { user: { name:  "",
                                         email: "user@invalid",
                                         password:              "foo",
                                         password_confirmation: "bar" } }
    end
    assert_template 'users/new'
    assert_select 'div#error_explanation'
    assert_select 'div.field_with_errors'
  end

  test "valid signup information with account activation" do
    get signup_path
    assert_difference 'User.count', 1 do
      post users_path, params: { user: { name:  "Example User",
                                         email: "user@example.com",
                                         password:              "password",
                                         password_confirmation: "password" } }
    end
    assert_equal 1, ActionMailer::Base.deliveries.size
    user = assigns(:user)
    assert_not user.activated?
    # 有効化していない状態でログインしてみる
    log_in_as(user)
    assert_not is_logged_in?
    # 有効化トークンが不正な場合
    get edit_account_activation_path("invalid token", email: user.email)
    assert_not is_logged_in?
    # トークンは正しいがメールアドレスが無効な場合
    get edit_account_activation_path(user.activation_token, email: 'wrong')
    assert_not is_logged_in?
    # 有効化トークンが正しい場合
    get edit_account_activation_path(user.activation_token, email: user.email)
    assert user.reload.activated?
    follow_redirect!
    assert_template 'users/show'
    assert is_logged_in?
  end
end
```

#RSpec

###factoryのユーザーを有効にしておく

```rb:spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name {"Michael Example"}
    sequence(:email) { |n| "test#{n}@example.com" } 
    password {"password"}
    password_confirmation  {"password"}
    admin {true}
    activated {true}
    activated_at {Time.zone.now}

    trait :noadmin do
      admin {false}
    end

    trait :noactivated do
      admin {false}
      activated {false}
      activated_at { nil }
    end

    trait :users do
      sequence(:name) { |n| "User#{n}" } 
      sequence(:email) { |n| "testusers#{n}@example.com" }
      admin {false}
    end

  end
end
```

userのactivatedをtrueにしておく。
また、**ユーザー登録のテストにアカウント有効化を追加する**において使用する無効なユーザーであるnoactivatedユーザーを作成しておく。

###アカウント有効化のプレビューメソッド

```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
    UserMailer.password_reset
  end

end

```

###Userメイラーのテスト

```rb:spec/mailers/user_mailer_spec.rb
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

end
```

FactoryBotでユーザーを作成した際、Userモデルの`before_create :create_activation_digest`が実行されるため、`user.activation_token = User.new_token`は記述しなくて良い。

###Userテスト内の抽象化したauthenticated?メソッド

```rb:spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do

  before do
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar")
  end
  .
  .
  .
  it "authenticated? should return false for a user with nil digest" do
    expect(@user.authenticated?(:remember, "")).to be_falsy
  end

end
```

###ユーザー登録のテストにアカウント有効化を追加する
```rb:spec/requests/users_signups_spec.rb
require 'rails_helper'

RSpec.describe "UsersSignups", type: :request do

  before do
    ActionMailer::Base.deliveries.clear
  end

  let(:user) { FactoryBot.attributes_for(:user) }
  let(:noactivateduser) { FactoryBot.create(:user, :noactivated) }


  it "valid signup information" do
    expect {
      post users_path, params: { user: user }
    }.to change(User, :count).by(1)
    expect(response).to redirect_to root_url
    expect(is_logged_in?).to be_falsy
  end

  it "valid signup information with account activation" do
    expect(noactivateduser).to_not be_activated
    log_in_as(noactivateduser)
    expect(is_logged_in?).to be_falsy
    get edit_account_activation_path("invalid token", email: noactivateduser.email)
    expect(is_logged_in?).to be_falsy
    get edit_account_activation_path(noactivateduser.activation_token, email: "wrong")
    expect(is_logged_in?).to be_falsy
    get edit_account_activation_path(noactivateduser.activation_token, email: noactivateduser.email)
    assert noactivateduser.reload.activated?
    expect(noactivateduser).to be_activated
    expect(response).to redirect_to user_path(noactivateduser)
    expect(is_logged_in?).to be_truthy
  end

end
```

第8章において作成したrequest specに記述していく。
第8章では、`is logges_in?`メソッドを使用するためにsystem specの一部をrequest specの*"valid signup information"*に移動させたが、その中身を今回の内容に合わせて一部変更している。
注意すべきなのが、*"valid signup information"*を成功させるためには、`FactoryBot.attributes_for`でユーザーを作成する必要があるが、実際に、無効なユーザーのデータを使用するためには`FactoryBot.create`で作成していなければならないため、今回は二つのテストに分けて記述している。