【Rails6】RSpecによるユーザー新規登録、及びログインの結合テストの実装

#はじめに
サービスの品質を保つために必要不可欠なテストを実施しております。

今回はユーザー新規登録、及びログインの結合テストを実装し、その実装内容を記事にしていきたいと思います。

#前提

・Userモデルの単体テストは実施済み

※完了されていない方や単体テストと並行してご覧になられたい方は、以下のUserモデルの単体テストについての記事をご参考ください。

https://qiita.com/narimiya/items/8ec3cdc15b1134b04dce

#バージョン
rubyのバージョン　ruby-2.6.5
Railsのバージョン Rails:6.0.0
rspec-rails 4.0.0

#実施したテスト

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/1b7dbe98-7afa-c203-6e55-a429bde8f9d0.png)

#新規登録画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/f9f82232-f554-1843-510f-21b75727407b.png)


#ログイン画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/41d0960b-afa2-8ef4-580d-9677f69fcac3.png)

#usersテーブルのカラムの紹介

```xxxx_devise_create_users.rb

class DeviseCreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string  :email,                null: false, default: ""
      t.string  :encrypted_password,   null: false, default: ""
      t.string  :nickname,             null: false
      t.text    :occupation,           null: false
      t.text    :position,             null: false
      t.date    :birth_day,            null: false
      t.integer :sex_id,               null: false
      t.boolean :admin,                default: false

#〜省略〜
```

##モデル内のバリデーション

```app/models/user.rb

#〜省略〜

  validates :nickname, presence: true
  validates :occupation, presence: true
  validates :position, presence: true
  validates :birth_day, presence: true
  validates :image, presence: true

  with_options presence: true, numericality: { other_than: 1 } do
  validates :sex_id
  end


  PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?[\d])[a-z\d]+\z/i.freeze
  validates_format_of :password, with: PASSWORD_REGEX 
  
  VALID_PASSWORD_REGEX = /\A[a-z0-9]+\z/i
  validates :password, format: { with: VALID_PASSWORD_REGEX }

 validates :email, presence: true, uniqueness: { case_sensitive: true },
  length: { maximum: 255 }

#〜省略〜

```

##FactoryBotの内訳

```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    email                           {Faker::Internet.free_email}
    password                        {'abc1234'}
    password_confirmation           {'abc1234'}
    nickname                        {Faker::Name.initials(number: 2)}
    occupation                      {Faker::Company.name}
    position                        {Faker::Company.name}
    image                           {Rack::Test::UploadedFile.new(File.join(Rails.root, 'spec/factories/test.jpg')) }
    birth_day                       {Faker::Date.birthday}
    sex_id                          {Faker::Number.between(2, 5)} 
  end
end

```

##テストコードの内容

```spec/system/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 'user[email]', with: @user.email
      fill_in 'user[password]', with: @user.password
      fill_in 'user[password_confirmation]', with: @user.password_confirmation
      fill_in 'user[nickname]', with: @user.nickname
      fill_in 'user[occupation]', with: @user.occupation
      fill_in 'user[position]', with: @user.position
      page.attach_file('user[image]', Rails.root + 'spec/factories/test.jpg')
      select '1931',from: 'user[birth_day(1i)]'
      select '10',from: 'user[birth_day(2i)]'
      select '10',from: 'user[birth_day(3i)]'
      select 'Not known',from: 'user[sex_id]'

      # サインアップボタンを押すとユーザーモデルのカウントが1上がることを確認する
      expect{
        find('input[name="commit"]').click
      }.to change { User.count }.by(1)

      # トップページへ遷移したことを確認する
      expect(current_path).to eq root_path
    end
  end
  context 'ユーザー新規登録ができないとき' do
    it '誤った情報ではユーザー新規登録ができずに新規登録ページへ戻ってくる' do
      #トップページに移動する
      visit root_path

      # トップページにサインアップページへ遷移するボタンがあることを確認する
      expect(page).to have_content('新規登録')

      # 新規登録ページへ移動する
      visit new_user_registration_path

      # ユーザー情報を入力する
      fill_in 'user[email]',  with: ''
      fill_in 'user[password]', with: ''
      fill_in 'user[password_confirmation]', with: ''
      fill_in 'user[nickname]', with: ''
      fill_in 'user[occupation]', with: ''
      fill_in 'user[position]', with: ''
      page.attach_file('user[image]', Rails.root + '')
      select '--',from: 'user[birth_day(1i)]'
      select '--',from: 'user[birth_day(2i)]'
      select '--',from: 'user[birth_day(3i)]'
      select '--',from: 'user[sex_id]'

      # サインアップボタンを押してもユーザーモデルのカウントは上がらないことを確認する
      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 user_session_path

      # 正しいユーザー情報を入力する
      fill_in 'user[email]', with: @user.email
      fill_in 'user[password]', with: @user.password

      # ログインボタンを押す
      find('input[name="commit"]').click

      # トップページへ遷移することを確認する
      expect(current_path).to eq(root_path)
    end
  end
  context 'ログインができないとき' do
    it '保存されているユーザーの情報と合致しないとログインができない' do
      # トップページに移動する
      visit root_path

      # トップページにログインページへ遷移するボタンがあることを確認する
      expect(page).to have_content('ログイン')

      # ログインページへ遷移する
      visit user_session_path

      # ユーザー情報を入力する
      fill_in 'user[email]',  with: ''
      fill_in 'user[password]', with: ''

      # ログインボタンを押す
      find('input[name="commit"]').click

      # ログインページへ戻されることを確認する
      expect(current_path).to eq(new_user_session_path)
    end
  end
end
```

#補足説明
##結合テストとは
結合テストコードは、ユーザーがたどる一連の流れを確認するものです。

なので、モデル及びコントローラーの単体テストを一連でテストするものです。

単体テストコードが「点」であれば、結合テストは「線」となって、テストを行うようなものです。

##visitについて
visit 〇〇_pathのように記述すると、〇〇のページへ遷移することを表現できます。

##pageについて
visitで訪れた先のページの見える分だけの情報が格納されています。

##fill_inについて
fill_in 'フォームの名前', with: '入力する文字列'のように記述することで、フォームへの入力を行うことができます。

※fill_in○の○の要素には、name属性を記述します。
 name属性の確認方法は検証ツールを使用します。

##find(~).clickについて
find('クリックしたい要素').clickと記述することで、実際にクリックができます。

##changeについて
expect{ 何かしらの動作 }.to change { モデル名.count }.by(1)と記述することによって、モデルのレコードの数がいくつ変動するのかを確認できます。

##current_pathについて
現在いるページのパスを示します。

##page.attach_fileについて
Active Storageを使用した画像挿入を行う際に使用します。

##selectについて
プルダウン形式で値を入力するものに対してはselectを使用します。
　
selectの要素には実際に入れることのできる値を任意に選択します。
from以下はname属性の値記述します。

##異常系のselect '--'ついて
バリデーションではいずれの要素において、'--'を選択しないように設定しております。

なので、プルダウン形式で値を入力する項目の異常系に対しては'--'を選択するようにしております。

##テストコードの内容について
正常
・新規登録が上手くいく場合
・ログインが上手くいく場合

異常
・新規登録が上手くいかない場合
・ログインが上手くいかない場合

##参考情報
fill_inの要素を記述する際に、私エラーを起こし、そのエラーを解消したことで、学びを得ることができました。

その解決方法についても以下記事にしておりますので、よろしければご参考ください。

https://qiita.com/narimiya/items/819cc9acbeeff0210ce8

以上です。