【Ruby on Rails】factory_botを用いたモデルテストの実行手順（複数モデル干渉可）

#はじめに
個人アプリのテストをおざなりにしていましたが、現場に入って最初に行うであろう（予想）テスト業務を滞りなく行うために、学び直した備忘録。ここではモデルのテストについて記述します。

##RSpecの導入
まずはRSpecを利用するために、
・rspec-rails
そしてダミーのインスタンスを簡単に生成できる
・factory_bot

この２つをインストールします。

```ruby:Gemfile
group :development, :test do
#省略
  gem 'rspec-rails'
  gem 'factory_bot_rails'
end
```
bundle installを実行

```terminal:ターミナル
$ bundle install
```

##RSpecの設定
Rspec用の設定ファイルを下記コマンドで作成します。

```terminal:ターミナル
$ rails g rspec:install
```
そして生成された「.rspec」に下記を記述

```ruby:.rspec
--format documentation
```
出力をフォーマットする方法をRSpecに指示する記述です。テスト結果がすっきりと見やすくなります。

ここできちんと導入できているかチェックします。

```terminal:ターミナル
$ bundle exec rspec
```
以下の様な結果が出力されれば準備は完了です。

```terminal:ターミナル
No examples found.


Finished in 0.0006 seconds (files took 0.46206 seconds to load)
0 examples, 0 failures
```
##factory_botを使用する準備
まず「spec」ディレクトリ直下に「factries」というディレクトリを追加します。

そしてその「factories」内に「Model名の複数形.rb」という名前でファイルを生成して下さい。（例：items.rb)

実際のModelのファイル名と見分けがつかなくなることから複数形で作成することを推奨します。

ここは適宜自身のテストを行う、Model名に合わせて下さい。

今回は「items.rb」という名前で作成します。
下記の様に記述して下さい。

```ruby:items.rb
FactoryBot.define do

  factory :item do
    name              {"ネックレス"}
    status            {"K18ネックレスです"}
    brand             {"C.ARA"}
    user   #外部モデル
    after(:build) do |item|   #外部モデル（複数）
      item.images << build(:image, item: item)
    end 
  end

end
```

適宜それぞれアプリケーションのカラム名に合わせ、データをセットして下さい。
user, imagesにおいては、テストしたいModelに紐づいているModelです。
テストするModelに紐づいているモデルがあれば上記同様「factories」内に「Model名の複数形.rb」という名前でファイルを生成して下さい。

下記の２つのファイルはテストするModelに紐づいているModelです。

```ruby:users.rb
FactoryBot.define do
  
  factory :user do
    nickname {"こんちゃん"}
    image {"test.jpeg"}
    password {"12345678"}
    password_confirmation {"12345678"}
    #Fakerを使用している場合
    sequence(:email) {Faker::Internet.email}
    #していない場合
    email {"test@test.com"}
  end

end
```

```ruby:images.rb
FactoryBot.define do
  
  factory :image do
    src {"test.jpg"}
    item
  end

end
```

facrory_botを利用する事により、テストを書く際のインスタンスを生成する記述をかなり省略することができます。

```ruby
#factory_botを利用しない場合のインスタンスの生成方（外部キー省略）
item = Item.new(name: "ネックレス", status: "k18ネックレスです", brand: "C.ARA")
#factory_botを利用する場合
item = FactoryBot.build(:item)
```

そしてfactory_botの記述を省略するために下記の様に記述します。

```ruby:rails_helper.rb
#省略
RSpec.configure do |config|
  #下記追記
  config.include FactoryBot::Syntax::Methods
  #省略
end
```

##テストを書いていく
さぁテストを書いていきます。
今回はModelのテストを書くので、「spec」ディレクトリ以下に「models」ディレクトリを作成しさらにその中に「item_spec.rb」を作成しましょう。

ディレクトリ構造
spec/models/item_spec.rb

テストを記述します。
まずは基本的な全てのプロパティに値が入っているときに保存できるかをチェックします。

```ruby:item_spec.rb
require 'rails_helper'

describe Item do
  describe '#create' do

    it "allcomplete => save" do
      item = build(:item)
      expect(item).to be_valid
    end

  end
end
```
it ~ doの間はそのexampleの説明を記述します。（日本語でもOK）
上記はインスタンスを生成（factory_botにより）し、それが保存できるかをチェックするテストです。

テストを実行してみましょう。

```terminal:ターミナル
$ bundle exec rspec
```

下記の様になれば成功です。
エラーが出る場合はfactory_botのプロパティ名、データ型、またはバリデーションが間違っていないかチェックしましょう。

```terminal:ターミナル
Item
  #create
    allcomplete => save

Finished in 0.2394 seconds (files took 2.48 seconds to load)
1 example, 0 failures
```
では次にテスト項目を増やしテストを実行しましょう。

```item_spec.rb
require 'rails_helper'
describe Item do
  describe '#create' do

    it "allcomplete => save" do
      item = build(:item)
      expect(item).to be_valid
    end

    it "name must exist" do
      item = build(:item, name: nil)
      item.valid?
      expect(item.errors[:name]).to include("can't be blank")
    end

    it "status == nil otherComplete => save?" do
      item = build(:item, status:nil)
      expect(item).to be_valid
    end

    it "brand == nil otherComplete => save?" do
      item = build(:item, brand:nil)
      expect(item).to be_valid
    end

  end
end
```

上から順に

・全てに値が入っていれば保存できる
・nameに値が入っていなければ保存できない
　※解説
　・validメソッド
　　インスタンスを保存する際にlaravelによって保存できない様になっているかを確かめられる
　・errordメソッド
　　なぜ保存できないのかを確認できる
・statusに値が入っていなくても保存できる
・brandに値が入っていなくても保存できる

この四項目をテストしていきます。

```terminal:ターミナル
$ bundle exec rspec
```

```terminal:ターミナル
Item
  #create
    allcomplete => save
    name must exist
    status == nil otherComplete => save?
    brand == nil otherComplete => save?

Finished in 0.26776 seconds (files took 2.52 seconds to load)
4 examples, 0 failures
```

全てパスしました。

この様にfactory_botを使用することで、簡単にダミーデータを作成し、少ない記述で、かつ柔軟にテストを行うことができます。

どなたかの助けになれば幸いです。