railsで結合テスト記述する方法(ユーザー新規登録、ログイン)

# 開発環境
Mac OS Catalina 10.15.7
ruby 2.6系
rails 6.0系

# 前提

ユーザーモデルの単体テストコードは記述済みとする
rspec導入済み
FactoryBot導入済み
Capybara（カピバラ）導入済み(たぶんデフォルトでGemfileに入ってます。）

```
# これがCapybaraです
gem 'capybara', '>= 2.15'
```

# 結合テストを書くファイルを生成する

まず結合テストを書くファイルを作ります。

ターミナルに以下を打ちこみます。

```
rails g rspec:system users
```

すると、spec/systemディレクトリの中に users_spec.rb というファイルができるのでここにテストコードを記述していきます。

# exampleを洗い出す

ユーザーが操作をする流れに沿ってexampleを洗い出しましょう。
成功する場合と失敗する場合両方を記述する必要があります。
作成するアプリケーションの仕様によって内容が変わってくると思いますが、概ねこんな感じの流れになるかとは思います。

```users_spec.rb
require 'rails_helper'

RSpec.describe "ユーザー新規登録", type: :system do
  context 'ユーザー新規登録ができるとき' do 
    it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do
      # トップページに移動する
      # トップページにサインアップページへ遷移するボタンがあることを確認する
      # 新規登録ページへ移動する
      # ユーザー情報を入力する
      # 新規登録を押すとユーザーモデルのカウントが1上がることを確認する
      # 新規登録ページへ遷移したことを確認する
      # トップページにログアウトボタンやユーザーの名前が表示されることを確認する
      # 新規登録ページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する
    end
  end
  context 'ユーザー新規登録ができないとき' do
    it '誤った情報ではユーザー新規登録ができずにトップページに戻ってくる' do
      # トップページに移動する
      # トップページにサインアップページに遷移するボタンがあることを確認する
      # 新規登録ページへ移動する
      # ユーザー情報を入力する
      # サインアップボタンを押してもユーザーモデルのカウントが上がらないことを確認する
      # 新規登録ページへ戻ってくることを確認する
    end
  end
end

RSpec.describe 'ログイン', type: :system do
  context 'ログインができるとき' do
    it '保存されているユーザーの情報と合致すればログインができる' do
      # トップページに移動する
      # トップページにログインページに遷移するボタンがあることを確認する
      # ログインページに遷移する
      # 正しいユーザー情報を入力する
      # ログインボタンを押す
      # トップページに遷移する
      # トップページにログアウトボタンやユーザーの名前が表示されることを確認する
      # 新規登録ページに遷移するボタンやログインページに遷移するボタンがないことを確認する
    end
  end
  context 'ログインができないとき' do
    it '保存されているユーザーの情報と合致しないとログインができない' do
      # トップページに移動する
      # トップページにログインページに遷移するボタンがあることを確認する
      # ログインページに遷移する
      # 誤ったユーザー情報を入力する
      # ログインボタンを押す
      # ログインページへ戻されることを確認する
    end
  end
end
```

# FactoryBotでダミーデータを作成する

テストコードを書く際に必要になるので、FactoryBotでダミーデータを作成します。

```users_spec.rb
require 'rails_helper'

RSpec.describe "ユーザー新規登録", type: :system do

  before do
    @user = FactoryBot.build(:user)
  end

  context 'ユーザー新規登録ができるとき' do 
    it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do
      # トップページに移動する
      # トップページにサインアップページへ遷移するボタンがあることを確認する
      # 新規登録ページへ移動する
      # ユーザー情報を入力する
      # 新規登録を押すとユーザーモデルのカウントが1上がることを確認する
      # 新規登録ページへ遷移したことを確認する
      # トップページにログアウトボタンやユーザーの名前が表示されることを確認する
      # 新規登録ページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する
    end
  end
  context 'ユーザー新規登録ができないとき' do
    it '誤った情報ではユーザー新規登録ができずにトップページに戻ってくる' do
      # トップページに移動する
      # トップページにサインアップページに遷移するボタンがあることを確認する
      # 新規登録ページへ移動する
      # ユーザー情報を入力する
      # サインアップボタンを押してもユーザーモデルのカウントが上がらないことを確認する
      # 新規登録ページへ戻ってくることを確認する
    end
  end
end

RSpec.describe 'ログイン', type: :system do

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

  context 'ログインができるとき' do
    it '保存されているユーザーの情報と合致すればログインができる' do
      # トップページに移動する
      # トップページにログインページに遷移するボタンがあることを確認する
      # ログインページに遷移する
      # 正しいユーザー情報を入力する
      # ログインボタンを押す
      # トップページに遷移する
      # トップページにログアウトボタンやユーザーの名前が表示されることを確認する
      # 新規登録ページに遷移するボタンやログインページに遷移するボタンがないことを確認する
    end
  end
  context 'ログインができないとき' do
    it '保存されているユーザーの情報と合致しないとログインができない' do
      # トップページに移動する
      # トップページにログインページに遷移するボタンがあることを確認する
      # ログインページに遷移する
      # 誤ったユーザー情報を入力する
      # ログインボタンを押す
      # ログインページへ戻されることを確認する
    end
  end
end
```

注意したいのは、新規登録をテストする場合ははbuildを、ログインをテストする場合はcreateを使うことです。
ログインをテストする場合は、一度データを保存して、その保存したデータの情報と合致するかどうかを記述する必要があるためcreateする必要があります。

# テストコード記述

完成したテストコードは以下の通りです。

```users_spec.rb
require 'rails_helper'

RSpec.describe "ユーザー新規登録", type: :system do
  before do
    @user = FactoryBot.build(:user)
  end

  context 'ユーザー新規登録ができるとき' do 
    it '正しい情報を入力すればユーザー新規登録ができてトップページに移動する' do
      # トップページに移動する
      visit root_path
      # トップページにサインアップページへ遷移するボタンがあることを確認する
      expect(page).to have_content('新規登録')
      # 新規登録ページへ移動する
      visit new_user_registration_path
      # ユーザー情報を入力する
      fill_in 'ニックネーム', with: @user.nickname
      fill_in 'メールアドレス', with: @user.email
      fill_in 'パスワード', with: @user.password
      fill_in 'パスワード(確認)', with: @user.password_confirmation
      # 新規登録を押すとユーザーモデルのカウントが1上がることを確認する
      expect{
        find('input[name="commit"]').click
      }.to change {User.count}.by(1)
      # 新規登録ページへ遷移したことを確認する
      expect(current_path).to eq root_path
      # トップページにログアウトボタンやユーザーの名前が表示されることを確認する
      expect(page).to have_content('ログアウト')
      expect(page).to have_content(@user.nickname)
      # 新規登録ページへ遷移するボタンや、ログインページへ遷移するボタンが表示されていないことを確認する
      expect(page).to have_no_content('新規登録')
      expect(page).to have_no_content('ログイン')
    end
  end
  context 'ユーザー新規登録ができないとき' do
    it '誤った情報ではユーザー新規登録ができずにトップページに戻ってくる' do
      # トップページに移動する
      visit root_path
      # トップページにサインアップページに遷移するボタンがあることを確認する
      expect(page).to have_content('新規登録')
      # 新規登録ページへ移動する
      visit new_user_registration_path
      # ユーザー情報を入力する
      fill_in 'ニックネーム', with: ""
      fill_in 'メールアドレス', with: ""
      fill_in 'パスワード', with: ""
      fill_in 'パスワード(確認)', with: ""
      # サインアップボタンを押してもユーザーモデルのカウントが上がらないことを確認する
      expect{
        find('input[name="commit"]').click
      }.to change {User.count}.by(0)
      # 新規登録ページへ戻ってくることを確認する
      expect(current_path).to eq "/users"
    end
  end
end

RSpec.describe 'ログイン', type: :system do
  before do
    @user = FactoryBot.create(:user)
  end
  context 'ログインができるとき' do
    it '保存されているユーザーの情報と合致すればログインができる' do
      # トップページに移動する
      visit root_path
      # トップページにログインページに遷移するボタンがあることを確認する
      expect(page).to have_content('ログイン')
      # ログインページに遷移する
      visit new_user_session_path
      # 正しいユーザー情報を入力する
      fill_in 'メールアドレス', with: @user.email
      fill_in 'パスワード', with: @user.password
      # ログインボタンを押す
        find('input[name="commit"]').click
      # トップページに遷移する
      visit root_path
      # トップページにログアウトボタンやユーザーの名前が表示されることを確認する
      expect(page).to have_content('ログアウト')
      expect(page).to have_content(@user.nickname)
      # 新規登録ページに遷移するボタンやログインページに遷移するボタンがないことを確認する
      expect(page).to have_no_content('新規登録')
      expect(page).to have_no_content('ログイン')
    end
  end
  context 'ログインができないとき' do
    it '保存されているユーザーの情報と合致しないとログインができない' do
      # トップページに移動する
      visit root_path
      # トップページにログインページに遷移するボタンがあることを確認する
      expect(page).to have_content('ログイン')
      # ログインページに遷移する
      visit new_user_session_path
      # 誤ったユーザー情報を入力する
      fill_in 'メールアドレス', with: ""
      fill_in 'パスワード', with: ""
      # ログインボタンを押す
      find('input[name="commit"]').click
      # ログインページへ戻されることを確認する
      expect(current_path).to eq "/users/sign_in"
    end
  end
end
```

マッチャ等の説明はしないので、気になったものや知らなかったものは調べていただければと思います。(rspec マッチャ　とか　マッチャ 〇〇　とかで調べるとすぐ出てくると思います。)

参考になれば幸いです。