[初心者log]Rails, RSpecにてPostの単体テスト

#本記事の目的
RSpec,Factry_botを用いてUserモデルと関連付いたPostモデルの単体テストを書いたので記事としてまとめておきます。

##Postのカラム
```xxx_create_posts.rb
class CreatePosts < ActiveRecord::Migration[6.1]
  def change
    create_table :posts do |t|
      t.integer :post_field
      t.string :content
      t.references :user, foreign_key: true

      t.timestamps
    end
    add_index :posts, [:user_id, :created_at]
  end
end
```
##Postのモデル
```app/model/post.rb
class Post < ApplicationRecord
  belongs_to :user
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
  validates :post_field, presence: true
end
```
##gem紹介
```Gemfile
・
・
・
group :development, :test do
  gem "rspec-rails"
  gem "factory_bot_rails"
end
```

##rails_helperの設定
```api/spec/rails_helper.rb
RSpec.configure do |config|
  #Factory_botのメソッドを使用する際に、クラス名の指定を省略
  config.include FactoryBot::Syntax::Methods #←追加
```


この記述により、

```rb
user = FactoryBot.create(:user)
```
といった記述をしたいとき、

```rb
user = create(:user)
```
といったように省略できるようになります。

##FactoryBot内の記述
```spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { "test" }
    sequence(:email) { |n| "test#{n}@example.com" }
    password { "password" }
    profile { "僕はhogehogeです。よろしくお願いいたします。" }
  end
end
```

```spec/factories/posts.rb
FactoryBot.define do
  factory :post do
    content { "test!" }
    post_field { Random.rand(1..10) }
    association :user
  end
end
```

###失敗例
```spec/models/post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  const "バリデーションについて" do
    it "user_id, content, post_fieldがあれば有効であること" do
      expect(create(:post)).to be_valid
    end
    
    it "user_idがなければ無効であること" do
      post = create(:post, user_id: nil)  
      post.valid?
      expect(post.errors[user_id]).to include("can't be blank")
    end

  ・
  ・
  ・
  end
end
```

このように記述してしまうと、
"user_id, content, post_fieldがあれば有効であること"で作成したpostと重複が発生してしまい、エラーになってしまいました。

###成功例

```spec/model/post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  before do
    @post = build(:post)
  end

  describe "正常値と異常値の確認について" do

    context "バリデーションについて" do
      it "user_id,content,post_fieldがあれば有効であること" do
        expect(create(:user)).to be_valid
        expect(@post.errors).to be_empty
      end

      it "contentがなければ無効であること" do
        @post.content = ""
        @post.valid?
        expect(@post.errors[:content]).to include("can't be blank")
      end

      it "user_idがなければ無効であること" do
        @post.user_id = nil
        @post.valid?
        expect(@post.errors[:user_id]).to include("can't be blank")
      end

      it "post_fieldがなければ無効であること" do
        @post.post_field = nil
        @post.valid?
        expect(@post.errors[:post_field]).to include("can't be blank")
      end
    end

    context "他のモデルとのアソシエーションについて" do
      let(:association) do
        described_class.reflect_on_association(target)
      end

      context "Userモデルとのアソシエーション" do
        let(:target) { :user }
  
        it "Userとの関連付けはbelongs_toであること" do
          expect(association.macro).to  eq :belongs_to
        end
      end
    end
  end
end
```

事前にbeforeでpostをbuild（DBに保存されない）しておいて、のちにuserをcreate（DBに保存される）すればうまくいきました。
specの中でcreateを連発するのはあまりよろしくないと学びました。

##参考になった記事

[【Rails6】 RSpecによるReviewのモデル単体テストの実装](https://qiita.com/narimiya/items/6f6a7d1d383d57512d06)