【Rails6】 RSpecによるRelationshipのモデル単体テストの実装

#はじめに
サービスの品質を保つために必要不可欠なテストを実施しております。
今回はRelationship（フォロー）モデル編ということで、今後他のモデルについても実施し記事にしていきたいと思います。

※Relationshipモデル以外の記述に関しては省略しております。
後日通知機能についての記事を上げるため、その際に詳細を載せます。

#前提
・以下のgemはインストール済み

```
  gem 'rspec-rails', '~> 4.0.0'
  gem 'factory_bot_rails'
  gem 'faker'
```

・フォロー機能実装済み

#バージョン
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/9c56aa67-60f9-2ba2-b6a8-94ed25ef394a.png)

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

```xxx_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[6.0]
  def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :followed_id
      t.timestamps
    end
  end
end
```

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

```app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: 'User'
  belongs_to :followed, class_name: 'User'

  validates :follower_id, presence: true, uniqueness: { scope: :followed_id }
  validates :followed_id, presence: true
end
```

#FactoryBotの内訳

```spec/factories/rerationships.rb
FactoryBot.define do
  factory :relationship do
    follower_id    { FactoryBot.create(:user).id }
    followed_id    { FactoryBot.create(:user).id }
  end
end
```

#テストコードの内容

```spec/models/relationship_spec.rb
require 'rails_helper'

RSpec.describe Relationship, type: :model do
  let(:relationship) { FactoryBot.create(:relationship) }
  describe '#create' do
    context "保存できる場合" do
      it "全てのパラメーターが揃っていれば保存できる" do
        expect(relationship).to be_valid
      end
    end

    context "保存できない場合" do
      it "follower_idがnilの場合は保存できない" do
        relationship.follower_id = nil
        relationship.valid?
        expect(relationship.errors[:follower_id]).to include("を入力してください")
      end

      it "followed_idがnilの場合は保存できない" do
        relationship.followed_id = nil
        relationship.valid?
        expect(relationship.errors[:followed_id]).to include("を入力してください")
      end
    end

    context "一意性の検証" do
      before do
        @relation = FactoryBot.create(:relationship)
        @user1 = FactoryBot.build(:relationship)
      end
      it "follower_idとfollowed_idの組み合わせは一意でなければ保存できない" do
        relation2 = FactoryBot.build(:relationship, follower_id: @relation.follower_id, followed_id: @relation.followed_id)
        relation2.valid?
        expect(relation2.errors[:follower_id]).to include("はすでに存在します")
      end

      it "follower_idが同じでもfollowed_idが違うなら保存できる" do
        relation2 = FactoryBot.build(:relationship, follower_id: @relation.follower_id, followed_id: @user1.followed_id)
        expect(relation2).to be_valid
      end

      it "follower_idが違うならfollowed_idが同じでも保存できる" do
        relation2 = FactoryBot.build(:relationship, follower_id: @user1.follower_id, followed_id: @relation.followed_id)
        expect(relation2).to be_valid
      end
    end
  end

  describe "各モデルとのアソシエーション" do
    let(:association) do
      described_class.reflect_on_association(target)
    end

    context "仮想モデルfollowerとのアソシエーション" do
      let(:target) { :follower }

      it "Followerとの関連付けはbelongs_toであること" do
        expect(association.macro).to  eq :belongs_to
      end
    end

    context "仮想モデルfollowedとのアソシエーション" do
      let(:target) { :followed }

      it "Followedとの関連付けはbelongs_toであること" do
        expect(association.macro).to  eq :belongs_to
      end
    end
  end
end

```

#補足説明
##テストコードの内容について
以下のように、テストを大きく４つに分けて実施しました。
①保存できる場合の正常系
②保存できない場合の異常系
③一意性の担保
④各モデルとのアソシエーション

###**①、②について**
follower_idとfollowed_idの有無によるバリデーションを行っています。

###**③について**
follower_idとfollowed_idの組み合わせのバリデーションを行っています。
視点としてはフォローされる側とフォローする側大きく２つあります。

あるユーザーを@user1と仮定しました。

自身と異なるidの場合フォローができる、すなわち、自身はフォローができないことのテストを行っています。

また、フォローする側は同じ人には１回しかフォローができないテストも行っています。

###**④について**
各モデルとのアソシエーションのバリデーションを行っています。

以上です。