[Rails] Rspec ポートフォリオに実装したmodel spec

# はじめに
ポートフォリオに実際に実装したmodel specの部分です。
テストコードについては勉強したけどテストコードを実際に書く時にどんなことをテストすればいいか分からないと思ったのでそういった人の参考になれば嬉しいです。
テストしたのはログイン、CRUD、いいね機能部分です。


## User

validationのformatで指定している正規表現は、メールアドレスの形になっているのかを表しています。
Railsチュートリアルに載ってたのをまんまコピペしてきただけですが‥

```ruby:app/models/user.rb
 VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.freeze

 validates :email, presence: true, uniqueness: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }
```

```ruby:spec/factories/user.rb
FactoryBot.define do
  factory :user do
    email { Faker::Internet.email }
    password { 'password' }
  end
end 
```

```ruby:spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe "正常系の機能" do
    context "正しく入力さている場合" do
      let(:user) { build(:user) }
      it "保存できる" do
        expect(user.valid?).to eq true
      end
    end
  end

  describe "バリデーション" do
    subject { user.valid? }
    context "email が空のとき" do
      let(:user) { build(:user, email: "") }
      it "保存できない" do
        expect(subject).to eq false
        expect(user.errors.messages[:email]).to include "を入力してください"
      end
    end
    context "email が256文字以上のとき" do
      let(:user) { build(:user, email: "a" * 256) }
      it "保存できない" do
        expect(subject).to eq false
        expect(user.errors.messages[:email]).to include "は255文字以内で入力してください"
      end
    end
    context "email がすでに存在するとき" do
      before { create(:user, email: "test@example.com") }
      let(:user) { build(:user, email: "test@example.com") }
      it "保存できない" do
        expect(subject).to eq false
        expect(user.errors.messages[:email]).to include "はすでに存在します"
      end
    end
    context "email が アルファベット･英数字 のみのとき" do
      let(:user) { build(:user, email: Faker::Lorem.characters(number: 16)) }
      it "保存できない" do
        expect(subject).to eq false
        expect(user.errors.messages[:email]).to include "は不正な値です"
      end
    end
  end
end
```

### factory_botについて
factory_botですが、呼び出す時に以下のように特定のカラムに対してダミーデータの書き換えが可能です。

```ruby
 let(:user) { build(:user, email: "") }
```
便利でよく使うので覚えておきましょう！
自分も実装するまで知らなかった 笑

## Event
Event部分は定番の投稿部分です！
`day_after_today`はイベント開催日を今日より前には指定できなくする制限です。

```ruby:app/models/event.rb
  validates :name, presence: true, length: { minimum: 2, maximum: 50 }
  validates :text, presence: true
  validates :store, length: { minimum: 2, maximum: 50 }
  validates :address, length: { minimum: 2, maximum: 50 }
  validate :day_after_today

  def day_after_today
    errors.add(:base, '開催日程は今日より前の日は指定できません') if start_time.present? && start_time < Date.today
  end
```

```ruby:spec/factories/event.rb
FactoryBot.define do
  factory :event do
    name { Faker::Lorem.characters(number: 10) }
    text { Faker::Lorem.characters(number: 100) }
    store { Faker::Lorem.characters(number: 20) }
    address { Faker::Lorem.characters(number: 20) }
    start_time { Date.today }
    category_id { 1 }
    user
  end
end
```

```ruby:spec/models/event_spec.rb
require 'rails_helper'

RSpec.describe Event, type: :model do
  let(:user) { build(:user) }
  describe "正常系の機能" do
    context "正しく入力さている場合" do
      let(:event) { build(:event) }
      it "保存できる" do
        expect(event.valid?).to eq true
      end
    end
  end

  describe "バリデーション" do
    subject { event.valid? }
    context "name が空の場合" do
      let(:event) { build(:event, name: "") }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:name]).to include "を入力してください"
      end
    end
    context "name が2文字以下の場合" do
      let(:event) { build(:event, name: Faker::Lorem.characters(number: 1) ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:name]).to include "は2文字以上で入力してください"
      end
    end
    context "name が50文字以上の場合" do
      let(:event) { build(:event, name: Faker::Lorem.characters(number: 51) ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:name]).to include "は50文字以内で入力してください"
      end
    end
    context "text が空の場合" do
      let(:event) { build(:event, text: "") }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:text]).to include "を入力してください"
      end
    end
    context "store が2文字以下の場合" do
      let(:event) { build(:event, store: Faker::Lorem.characters(number: 1) ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:store]).to include "は2文字以上で入力してください"
      end
    end
    context "store が50文字以上の場合" do
      let(:event) { build(:event, store: Faker::Lorem.characters(number: 51) ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:store]).to include "は50文字以内で入力してください"
      end
    end
    context "address が2文字以下の場合" do
      let(:event) { build(:event, address: Faker::Lorem.characters(number: 1) ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:address]).to include "は2文字以上で入力してください"
      end
    end
    context "address が50文字以上の場合" do
      let(:event) { build(:event, address: Faker::Lorem.characters(number: 51) ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:address]).to include "は50文字以内で入力してください"
      end
    end
    context "start_time が今日より前の日付の場合" do
      let(:event) { build(:event, start_time: "2000-07-30" ) }
      it "保存できない" do
        expect(subject).to eq false
        expect(event.errors.messages[:base]).to include "開催日程は今日より前の日は指定できません"
      end
    end
  end
end
```

## Clip

Clipは多対多のいわゆるいいね機能と同じ機能です。
ここではIDが存在しないと保存できないっていうテストをしています。

```ruby:app/models/clip.rb
 validates :user_id, presence: true, uniqueness: { scope: :event_id }
 validates :event_id, presence: true
```

```ruby:spec/factories/clip.rb
FactoryBot.define do
  factory :clip do
    user_id { FactoryBot.create(:user).id }
    event_id { FactoryBot.create(:event).id }
  end
end
```

```ruby:spec/models/clip_spec.rb
require 'rails_helper'

RSpec.describe Clip, type: :model do
  subject { clip.valid? }
  describe "正常の機能" do
    context "データが条件を満たす場合" do
      let(:clip) { build(:clip) }
      it "保存できる" do
        expect(subject).to eq true
      end
    end
  end

  describe "バリデーション" do
    context "user_idが存在しない場合" do
      let(:clip) { build(:join, user_id: nil) }
      it "保存できない" do
        expect(subject).to eq false
        expect(clip.errors.messages[:user]).to include "を入力してください"
      end
    end
    context "event_idが存在しない場合" do
      let(:clip) { build(:join, event_id: nil) }
      it "保存できない" do
        expect(subject).to eq false
        expect(clip.errors.messages[:event]).to include "を入力してください"
      end
    end
    context "user_idが既に存在する場合" do
      let(:user) { create(:user) }
      let(:event) { create(:event, user_id: user.id) }
      before do
        create(:clip, user_id: user.id, event_id: event.id)
      end
      let(:clip) { build(:clip, user_id: user.id, event_id: event.id) }

      it "保存できない" do
        expect(subject).to eq false
        expect(clip.errors.messages[:user_id]).to include "はすでに存在します"
      end
    end
  end
end

```

以上です！
実際にコードレビューもしてもらったので問題ないはず！
テストはこんな感じで書くんだなーっていう雰囲気が伝われば嬉しいです！

次はrequest spec!