【Rails6】RSpecによるレビュー機能の結合テストの実装

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

今回はレビュー機能の結合テストを実装し、その実装内容を記事にしていきたいと思います。

#前提
・レビュー機能は実装済み
・Review（レビュー）モデルの単体テストは実施済み

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

https://qiita.com/narimiya/items/6f6a7d1d383d57512d06

#バージョン
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/83136eb4-c5ff-0fe8-3336-f5e2f76820f7.png)

#レビュー画面
![レビュー.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/b7acbdd0-286a-5a84-f0c1-02ac96d9a944.gif)

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

```xxx_create_reviews.rb
class CreateReviews < ActiveRecord::Migration[6.0]
  def change
    create_table :reviews do |t|
      t.string :content
      t.integer :score
      t.references :user,       foreign_key: true
      t.references :definition, foreign_key: true
      t.timestamps
    end
  end
end
```

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

```app/models/review.rb
class Review < ApplicationRecord
  belongs_to :user
  belongs_to :definition

  validates :user_id, presence: true
  validates :definition_id, presence: true
  validates :score, presence: true
  validates_uniqueness_of :definition_id, scope: :user_id
end
```

#FactoryBotの内訳

```spec/factories/reviews.rb
FactoryBot.define do
  factory :review do
    association :definition
    association :user
    content                       {Faker::Lorem.word}
    score                         {'3'}
  end
end
```

#サポートモジュール

```spec/support/review_up_support.rb
module ReviewUpSupport
  def review_up(review)
  # レビュー作成ページに遷移する
  visit new_definition_review_path(@definition)

  # レビューの存在の確認
  expect(page).to have_selector '#star1'
  expect(page).to have_selector '#star2'
  expect(page).to have_selector '#star3'
  expect(page).to have_selector '#star4'
  expect(page).to have_selector '#star5'

  # 星マークにカーソルを合わた場所でクリックする（今回は3段階）
  expect(find('#star3').hover.click)

  # レビューのコメントに情報を入力する
  fill_in 'review[content]', with: review.content

  # レビューのコメントを送信すると、Reviewモデルのカウントが1上がることを確認する
  expect{
    find('input[name="commit"]').click
  }.to change { Review.count }.by(1)
  end
end
```

#テストコードの内容

```spec/system/reviews_spec.rb
require 'rails_helper'

RSpec.describe "Reviews", type: :system, js: true do
  before do
    @definition = FactoryBot.create(:definition)
    @review = FactoryBot.create(:review)
  end
    describe '#create,#destroy' do
      context 'レビューができるとき' do
        it 'ユーザーが投稿に対してレビューができる' do
        # ログインする
        sign_in(@definition.user)
        
        #新規レビューを作成する
        review_up(@review)

        # # レビュー詳細ページに遷移する
        visit definition_reviews_path(@definition)

        # レビュー詳細ページにてレビューした投稿があることを確認する
        expect(page).to have_content(@review.score)
        expect(page).to have_content(@review.content)
        end
      end

      context 'レビューができないとき' do
        it 'ユーザーが投稿に対してレビューをすでにしており、レビューができない' do
         # ログインする
         sign_in(@definition.user)
          
         # レビュー作成ページに遷移する
         visit new_definition_review_path(@definition)
 
         #新規レビューを作成する
         review_up(@review)
           
         # レビュー作成ページに遷移する
         visit new_definition_review_path(@definition)

         # 星マークにカーソルを合わた場所でクリックする（今回は3段階）
         expect(find('#star3').hover.click)
 
         # レビューのコメントに情報を入力する
         fill_in 'review[content]', with: @review.content
           
         # レビューのコメントを送信すると、Reviewモデルのカウントが上がらないことを確認する
         expect{
          find('input[name="commit"]').click
        }.to change { Review.count }.by(0)

        # レビューページへ戻されることを確認する
         expect(current_path).to eq(definition_reviews_path(@definition))
        end
      end
   end
end
```

#補足説明
## #starについて
星マークを記述しているview内のidであり、今回は5つあります。
テストの関係上idを区別する必要があったため番付しています。

```app/views/reviews/index.html.erb
# 〜省略〜
      <span class="fa fa-star-o" id="star1"></span>
      <span class="fa fa-star-o" id="star2"></span>
      <span class="fa fa-star-o" id="star3"></span>
      <span class="fa fa-star-o" id="star4"></span>
      <span class="fa fa-star-o" id="star5"></span>
# 〜省略〜
```

'#'を文頭につけることでidを指し示すことができます。

##異常系のReview.countについて
モデル内にuniqunessのバリデーションをかけており、レビューは１回しかできない仕様にしています。

```app/models/review.rb
validates_uniqueness_of :definition_id, scope: :user_id
```

したがって一度レビューし、再度レビューしてもレビューカウントは上がらず0のままを証明できれば、レビューができていないことが言えます。

以上です。