【Ruby on Rails】RSpecでのモデルテスト

# 開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

# 前提
[【Ruby on Rails】RSpec導入まで](https://qiita.com/japwork/items/2a499c5f09cd53838d84)
こちらが出来ている前提で進めます。


# modelのテスト準備
##・バリデーションの設定
今回はタイトルカラムがあるかどうかと文字数のテストします。

```rb:app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  validates :title, presence: true, length: {maximum: 20}
end
```

##・ファイルの作成
①
spec配下にmodelsフォルダとfactoriesフォルダを作成し、
テストしたいモデルのファイルも作成します。
今回はpostモデルをテストします。
またユーザーがログインしている状態でしか投稿できないようにするために、
useモデルも作成します。
ファイル構成は下記の状態です。
　
spec/models/post_spec.rb
　→テストしたい内容を記述します

spec/factories/post.rb
spec/factories/user.rb
　→ダミーデータを作成します

②
FactoryBotを使えるようにします。
使用するとuser = create(:user) のようにDB登録やモデルのビルドができるため便利です。
spec配下にsupportフォルダとfactory_bot.rbファイルを作成し、下記のように記述します。

```rb:spec/support/factory_bot.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
end
```
その後、下記を追加します。

```rb:spec/rails_helper.rb
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# Prevent database truncation if the environment is production
abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails'
require 'support/factory_bot' # ＜ーーー　追加

...

```


# 実際のコード
まずはダミーデータを作成します。

```rb:spec/factories/user.rb
FactoryBot.define do
  factory :user do
    email { Faker::Internet.email }
    phone_number { 12345678909 }
    password { 'password' }
    password_confirmation { 'password' }
  end
end
```
```rb:spec/factories/post.rb
FactoryBot.define do
  factory :post do
    body { Faker::Lorem.characters(number:20) }
    user
  end
end
```

次にテストコードを記述します。

```rb:spec/models/post_spec.rb
require 'rails_helper'

RSpec.describe 'Postモデルのテスト', type: :model do
  describe 'バリデーションのテスト' do
    # factoriesで作成したダミーデータを使用します。
    let(:user) { FactoryBot.create(:user) }
    let!(:post) { build(:post, user_id: user.id) }

    # test_postを作成し、空欄での登録ができるか確認します。
    subject { test_post.valid? }
    let(:test_post) { post }


    context 'titleカラム' do
      it '空欄でないこと' do
        test_post.title = ''
        is_expected.to eq false;
      end
      it '20文字以下であること' do
        post.title = Faker::Lorem.characters(number:21)
        expect(post.valid?).to eq false;
      end
    end
  end
  describe 'アソシエーションのテスト' do
    context 'customerモデルとの関係' do
      it 'N:1となっている' do
        expect(Post.reflect_on_association(:user).macro).to eq :belongs_to
      end
    end

    # has_manyの関係性で記述するのもありです。
    # context 'PostCommentモデルとの関係' do
      # it '1:Nとなっている' do
        # expect(Post.reflect_on_association(:post_comments).macro).to eq :has_many
      # end
    # end
  end
end
```

その後、ターミナルで下記を実行してください。

```ターミナル
$ rspec spec/models
```

テストを通過すると

```
Finished in 0.52408 seconds (files took 2.11 seconds to load)
3 examples, 0 failures
```

このように表示されるためテスト内容が正しいことを表しています。

逆にテストを通過しない場合、このような形でどこでエラーが起きているかわかるので、
テストコードが間違っているのか、バリデーションが間違っているかなどがわかるようになります。

```
Failures:

  1) Postモデルのテスト バリデーションのテスト titleカラム 20文字以下であること
     Failure/Error: let!(:post) { build(:post) }
     
     NoMethodError:
       undefined method `build' for #<RSpec::ExampleGroups::Post::Nested::Title:0x000000000619e938>
     # ./spec/models/post_spec.rb:9:in `block (3 levels) in <top (required)>'

  2) Postモデルのテスト バリデーションのテスト titleカラム 空欄でないこと
     Failure/Error: let!(:post) { build(:post) }
     
     NoMethodError:
       undefined method `build' for #<RSpec::ExampleGroups::Post::Nested::Title:0x0000000007491518>
     # ./spec/models/post_spec.rb:9:in `block (3 levels) in <top (required)>'
Finished in 0.07992 seconds (files took 2.41 seconds to load)
2 examples, 2 failures

Failed examples:

rspec ./spec/models/post_spec.rb:11 # Postモデルのテスト バリデーションのテスト titleカラム 20文字以下であること
rspec ./spec/models/post_spec.rb:15 # Postモデルのテスト バリデーションのテスト titleカラム 空欄でないこと
```

また下部にあるrspec ./spec/models/post_spec.rb:11を使い、
下記のように個別にテスト内容を確認することも出来ます。

```:ターミナル
$ rspec spec/models/post_spec.rb:11
```

# まとめ
今回は
１，空白での登録を防ぐバリデーション　presence: true
２，文字数制限のバリデーション　length: {maximum: 20}
３，リレーションの関係性の確認　belongs_to :user

上記をテストしましたが、この他にも色々テスト方法があるため、
興味のある方は調べてみてください。

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら　https://twitter.com/japwork