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

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

#Minitest

###編集に対するテスト

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

class UsersEditTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "unsuccessful edit" do
    log_in_as(@user)
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: { name:  "",
                                              email: "foo@invalid",
                                              password:              "foo",
                                              password_confirmation: "bar" } }

    assert_template 'users/edit'
  end

  test "successful edit" do
    log_in_as(@user)
    get edit_user_path(@user)
    assert_template 'users/edit'
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name,  @user.name
    assert_equal email, @user.email
  end

  test "successful edit with friendly forwarding" do
    get edit_user_path(@user)
    log_in_as(@user)
    assert_redirected_to edit_user_url(@user)
    name  = "Foo Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { name:  name,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal name,  @user.name
    assert_equal email, @user.email
  end
end
```

###editとupdateアクションの保護に対するテスト

```rb:test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

  def setup
    @user       = users(:michael)
    @other_user = users(:archer)
  end

  test "should get new" do
    get signup_path
    assert_response :success
  end

  test "should redirect index when not logged in" do
    get users_path
    assert_redirected_to login_url
  end
  
  test "should redirect edit when not logged in" do
    get edit_user_path(@user)
    assert_not flash.empty?
    assert_redirected_to login_url
  end

  test "should redirect update when not logged in" do
    patch user_path(@user), params: { user: { name: @user.name,
                                              email: @user.email } }
    assert_not flash.empty?
    assert_redirected_to login_url
  end

  test "should redirect edit when logged in as wrong user" do
    log_in_as(@other_user)
    get edit_user_path(@user)
    assert flash.empty?
    assert_redirected_to root_url
  end

  test "should redirect update when logged in as wrong user" do
    log_in_as(@other_user)
    patch user_path(@user), params: { user: { name: @user.name,
                                              email: @user.email } }
    assert flash.empty?
    assert_redirected_to root_url
  end

  test "should redirect destroy when not logged in" do
    assert_no_difference 'User.count' do
      delete user_path(@user)
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when logged in as a non-admin" do
    log_in_as(@other_user)
    assert_no_difference 'User.count' do
      delete user_path(@user)
    end
    assert_redirected_to root_url
  end

end
```

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

archer:
  name: Sterling Archer
  email: duchess@example.gov
  password_digest: <%= User.digest('password') %>

lana:
  name: Lana Kane
  email: hands@example.gov
  password_digest: <%= User.digest('password') %>

malory:
  name: Malory Archer
  email: boss@example.gov
  password_digest: <%= User.digest('password') %>

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

###UsersIndexのテスト
```rb:test/integration/users_index_test.rb
require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @admin     = users(:michael)
    @non_admin = users(:archer)
  end

  test "index as admin including pagination and delete links" do
    log_in_as(@admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination'
    first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.name
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end
    assert_difference 'User.count', -1 do
      delete user_path(@non_admin)
    end
  end

  test "index as non-admin" do
    log_in_as(@non_admin)
    get users_path
    assert_select 'a', text: 'delete', count: 0
  end
end
```

#RSpec

###編集に対するテスト

```rb:spec/system/users_edit_spec.rb
require 'rails_helper'

RSpec.describe "UsersEdits", type: :system do

  let(:user) {FactoryBot.create(:user)}

  it "unsuccessful edit" do
    visit login_path
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"
    click_on "Setting"
    expect(page).to have_current_path edit_user_path(user)
    fill_in "Name", with: ""
    fill_in "Email", with: "user@invalid"
    fill_in "Password", with: "foo"
    fill_in "Confirmation", with: "bar"
    click_on "Save changes"
    expect(page).to have_current_path user_path(user)
  end

  it "successful edit" do
    visit login_path
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"
    click_on "Setting"
    expect(page).to have_current_path edit_user_path(user)
    fill_in "Name", with: "Foo Bar"
    fill_in "Email", with: "foo@bar.com"
    fill_in "Password", with: ""
    fill_in "Confirmation", with: ""
    click_on "Save changes"
    expect(page).to have_content("Profile updated")
    expect(page).to have_current_path user_path(user)
    user.reload
    expect(user.name).to eq "Foo Bar"
    expect(user.email).to eq "foo@bar.com"
  end

  it "successful edit with friendly forwarding" do
    visit edit_user_path(user)
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Log in"
    expect(page).to have_current_path edit_user_path(user)
    fill_in "Name", with: "Foo Bar"
    fill_in "Email", with: "foo@bar.com"
    fill_in "Password", with: ""
    fill_in "Confirmation", with: ""
    click_on "Save changes"
    expect(page).to have_content("Profile updated")
    expect(page).to have_current_path user_path(user)
    user.reload
    expect(user.name).to eq "Foo Bar"
    expect(user.email).to eq "foo@bar.com"
  end

end
```

###editとupdateアクションの保護に対するテスト

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

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

  before do
  @user = FactoryBot.create(:user)
  @noadmin_user = FactoryBot.create(:user, :noadmin)
  end

  it "returns http success" do
    get signup_path
    expect(response).to have_http_status(:success)
  end

  it "should redirect index when not logged in" do
    get users_path
    expect(response).to redirect_to login_path
  end

  it "should redirect edit when not logged in" do
    get edit_user_path(@user)
    expect(flash[:danger]).to eq "Please log in."
    expect(response).to redirect_to login_url
  end

  it "should redirect update when not logged in" do
    patch user_path(@user), params: { user: { name: @user.name,
    email: @user.email } }
    expect(flash[:danger]).to eq "Please log in."
    expect(response).to redirect_to login_url
  end

  it "should redirect edit when logged in as wrong user" do
    log_in_as(@noadmin_user)
    get edit_user_path(@user)
    expect(flash[:danger]).to eq nil
    expect(response).to redirect_to root_url
  end

  it "should redirect update when logged in as wrong user" do
    log_in_as(@noadmin_user)
    patch user_path(@user), params: { user: { name: @user.name,
    email: @user.email } }
    expect(flash[:danger]).to eq nil
    expect(response).to redirect_to root_url
  end

  it"should redirect destroy when not logged in" do
    expect {
      delete user_path(@user)
    }.to_not change(User, :count)
    expect(response).to redirect_to login_url
  end

  it "should redirect destroy when logged in as a non-admin" do
    log_in_as(@noadmin_user)
    expect {
      delete user_path(@user)
    }.to_not change(User, :count)
    expect(response).to redirect_to root_url
  end

end
```

`let`を使用してユーザーを遅延読み込みしてしまうと、最後の二つのテスト内の`expect {delete user_path(@user)}.to_not change(User, :count)`において結果的にユーザー数が変わってしまいエラーになるため今回は`before`で代用。

###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}

    trait :noadmin do
      admin {false}
    end

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

  end
end
```

traitを使用し、adminがfalseである非管理者のnoadmin、シーケンスを用いたnameとemailを持ち非管理者のusersを作成。

###UsersIndexのテスト

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

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

  before do
    30.times {FactoryBot.create(:user, :users)}
    @admin_user = FactoryBot.create(:user)
    @noadmin_user = FactoryBot.create(:user, :noadmin)
  end

  it "index as admin including pagination and delete links" do
    log_in_as(@admin_user)
    get users_path
    expect(response).to have_http_status(200)
    assert_select "div.pagination"
    first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select "a[href=?]", user_path(user), text: "delete"
      unless user == @admin_user
        assert_select "a[href=?]", user_path(user), text: "delete"
      end
    end
    expect {
      delete user_path(@noadmin_user)
    }.to change(User, :count).by(-1)
  end

  it "index as non-admin" do
    log_in_as(@noadmin_user)
    get users_path
    assert_select "a", text: "delete", count: 0
  end

end
```

**editとupdateアクションの保護に対するテスト**と同理由で、`let`を`before`で代用。`before do`内において、**factory**で作成したusersを三十人分複製し、ページネーションのテストに使用する。