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

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

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

#前提
・以下の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/b7e28ad4-9113-71d5-4b2d-c3fe861ea3d4.png)

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

```xxx_create_notifications.rb
class CreateNotifications < ActiveRecord::Migration[6.0]
  def change
    create_table :notifications do |t|
      t.references :visitor, foreign_key:{ to_table: :users }, null: false
      t.references :visited, foreign_key:{ to_table: :users }, null: false
      
      t.references :definition, foreign_key: true
      t.references :answer, foreign_key: true
      
      t.string :action, null: false
      t.boolean :checked, null: false, default: false

      t.timestamps
    end
  end
end
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/61c40c84-3cb2-eee2-2182-05c412b78eba.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1052793/60c8acc7-1cd3-69d7-c7af-d92d68220e1c.png)

●位置関係
回答　←　いいね
投稿　←　レビュー

#モデル内のアソシエーション

```app/models/notification.rb
class Notification < ApplicationRecord
  default_scope -> { order(created_at: :desc) }
  belongs_to :visitor, class_name: "User", optional: true
  belongs_to :visited, class_name: "User", optional: true
  belongs_to :definition, optional: true
  belongs_to :answer, optional: true

end
```

#FactoryBotの内訳

```spec/factories/notifications.rb
FactoryBot.define do
  factory :notification do
  end
end
```

#テストコードの内容

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

RSpec.describe Notification, type: :model do
  describe "#create" do
    context "Answer関連の通知" do
      before do
        @answer = FactoryBot.build(:answer)
        @definition = FactoryBot.build(:definition)
      end

      it "回答が行われた場合に保存できる" do
        notification = FactoryBot.build(:notification, answer_id: @answer.id, answer_id: @answer.id, action: "answer")
        expect(notification).to be_valid
      end

      it "投稿が行われた場合に保存できる" do
        notification = FactoryBot.build(:notification, definition_id: @definition.id, definition_id: @definition.id, action: "definition")
        expect(notification).to be_valid
      end

      it "回答にいいねが行われた場合に保存できる" do
        notification = FactoryBot.build(:notification, answer_id: @answer.id, action: "like")
        expect(notification).to be_valid
      end

      it "投稿にレビューが行われた場合に保存できる" do
        notification = FactoryBot.build(:notification, definition_id: @definition.id, action: "review")
        expect(notification).to be_valid
      end

    context "フォロー関連の通知" do
      it "フォローが行われた場合に保存できる" do
        user1 = FactoryBot.build(:user)
        user2 = FactoryBot.build(:user)
        notification = FactoryBot.build(:notification, visitor_id: user1.id, visited_id: user2.id, action: "follow")
        expect(notification).to be_valid
      end
    end

  end

  describe "アソシエーションのテスト" do
    let(:association) do
      described_class.reflect_on_association(target)
    end

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

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

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

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

    context "visitorとのアソシエーション" do
      let(:target) { :visitor }

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

    context "visitedとのアソシエーション" do
      let(:target) { :visited }

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

```

#補足説明

##optional: trueについて
optional: trueはidを持たないオブジェクトを作成できるという意味合いがあります。

デフォルトでidを持たないオブジェクトは作成できないので、optional: trueをつけることで、idを持たなくてもオブジェクトを作成できるようにしています。

#テストコードの内容について
・全体像としては、アクション（投稿、回答、いいね、レビュー、フォロー）が行われると通知が行われ、通知情報が保存される仕組みです。

なので、アクションに紐づく正常系をテストしています。

・アソシエーションが組めているかのテストも実施しています。

以上です。