【Rails】factory-bot、rspecのセットアップや記入法など解説

## 背景
本格的に１人でテストコードを書くとなった際に、セットアップやファイルは何を指しているなど色々と全くわかっていなかったのでその備忘録。

## セットアップ
**■``rspec``のセットアップ**
基本的に[公式のREADME](https://github.com/rspec/rspec-rails#rspec-rails--)を参考に進めていく。

``Gemfile``の**開発環境**と**テスト環境**に``rspec-rails``を記入していく。

```Gemfile.
group :development, :test do
  gem 'rspec-rails'
end
```

厳密には開発環境には``rspec-rails``は入れなくても良いが、その場合には``RAILS_ENV=test ``にする必要がある。

``bundle　 install``後、ターミナルにて``rails g rspec：install``を実行。

```ターミナル.
$ bundle install

$ rails g rspec:install
 create .rspec
 create spec
 create spec / spec_helper.rb
 create spec / rails_helper.rb
```

**■``factory_bot``のセットアップ**
``rails``のテストで``model``を扱うのに便利な``FactoryBot``。
設定をしておくと``user = create(:user)``のような形でDB登録やモデルのビルドができる。

```Gemfile.
group :development, :test do
  gem 'factory_bot_rails'
end
```

```spec/rails_helper.rb
RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods
　　　　#このコードを加えることで、以下のようにrspecのテストコード中でFactory_botのメソッドを使用する際に、クラス名の指定を省略できるようになる
end

# 通常FactoryBotをつけないと、メソッドを呼べない
 user = FactoryBot.create(:user)

 #設定を追加することで、FactoryBotの記述が省略できる。
 user = create(:user)
```


``.rspecファイル``に**``--require spec_helper``**の一文が入っていなければ追記。``rails_helper``の読み込みを行っている。

```..rspecファイル.
--require spec_helper
--format documentation
#format documentationを入れると、RSpecで表示の出力を綺麗にすることができる
```

## ファイルの作成
今回はタスク投稿できるアプリで、``task``と``user``のモデルテストをしていく。
まずターミナルにて``task``と``user``のモデルのテストファイルを作成する。

```ターミナル.
$ rails g rspec:model task
      create  spec/models/task_spec.rb
      invoke  factory_bot
      create  spec/factories/tasks.rb

$ rails g rspec:model user
      create  spec/models/user_spec.rb
      invoke  factory_bot
      create  spec/factories/users.rb
```

結果ファイル構成はこうなる↓。
[![Image from Gyazo](https://i.gyazo.com/fb9ea5fba2d87a27c18506f078c5b5dd.png)](https://gyazo.com/fb9ea5fba2d87a27c18506f078c5b5dd)

## テストデータ作成
まずは``factories/users.rb``にてテストユーザーの作成をしていく。

```users.rb
FactoryBot.define do
  factory :user do
    sequence(:email) {|n| "user_#{n}@example.com"} 
    #↑これが呼び出されるたびに、nの部分に数字が一つずつ増えて入るため、一意性が保たれる
    password {'password'}
    password_confirmation {'password'}
  end
end
```
次に``factories/tasks.rb``にてテストタスクの作成。

```tasks.rb
FactoryBot.define do
  factory :task do
    sequence(:title, "title_1")
    #"title_1"とすることで、"title_◯"と数字が増えていく。
　　　　　　　　#メール同様一意性が保たれる    
    content {'content'}
    status {:todo}
    deadline {1.week.from_now}
    association :user
  end
end
```

**``association :user``**
例えば下記のような``let``を使ったコードがあったとする。

```rb
RSpec.describe Task, type: :model do
  let(:user) { FactoryBot.create(:user) }
  let(:task) { FactoryBot.create(:task,user: user) }
end
```

``task``を作成する際に、``user``も必要なため、``user: user``と記述する必要があるが、``association :user``をファクトリに書いておくと、コードがとてもスッキリする。(**has_many**と**belongs_to**の関係が``userオブジェクト``を自動的に作成してくれる。)

```rb
RSpec.describe Task, type: :model do
  let(:user) { create(:user) }
  let(:task) { create(:task,user: user) }
end

↓このように省略できる

RSpec.describe Task, type: :model do
  let(:task) { create(user) }
end
```

## テスト内容ファイル解説
``models/task_spec.rb``を例に、ファイル作成時にデフォルトで下記の文言が入っている。この解説を先にする。

```task_spec.rb
require 'rails_helper'
#spec/rails_helper.rb を読み込む為の設定

RSpec.describe Task, type: :model do
  pending "add some examples to (or delete) #{__FILE__}"
end
```

**``RSpec.describe Task, type: :model do ~ end``**
この間に記入したものが、``task``の``RSpec``を書く宣言をしている。

**``pending "add some examples to (or delete) #{__FILE__}"``**
``pending``というのは保留の意味で、実際にテストを書いていく際にまだテストをせずスキップさせたい際に使われる。

つまりここでは、 "add some examples to (or delete) #{__FILE__}" というテスト(仮)を保留としている。

このテンプレートが書かれた``ModelSpec``でテスト実行をすると下記が出てくる。

```
1 example, 0 failures, 1 pending

・1 example
1つのテストがある

・0 failures
失敗したテストは0だった

・1 pending
1つ保留のテストがあった
```
基本的にデフォルトの``pending``は要らないので、コメントアウトしておくなり、削除しておくなりしておく。

## テスト内容作成
### 記入の流れ
``specファイル``ではほとんど、``describe``/``context``/``it``を用いて記述する。

・``describe``にはテスト対象の「クラス」や「メソッド」などを記入。

・``context``にはテストしようとしている状況「ログイン中のユーザーの場合」などを記入。

・``it``は期待する結果を記入。

大まかな記入の流れは下記。

```example.rb
describe "バリデーションについて" do

  context "姓名が設定されている場合　"
    it "姓名が表示されること" do
      # ...
    end

  context "姓名が記入されていない場合"
    it "姓名を入力してくださいとエラーが出るようにする" do
      # ...
    end

　　　 context "..."
    it "..." do
    end
   .
   .
  end
```

### 実際に記入する際に使うマッチャ（matcher）について
マッチャ（matcher）は「期待値と実際の値を比較して、一致した（もしくは一致しなかった）という結果を返すオブジェクト」のこと。

```rb
describe "四則演算" do
 context "足し算"
   it "1+2の合計は３になること" do
     expect(1 + 2).to eq 3
   end
end
```

上記のコードを例に見てみると、直感的に内容が分かりやすくなっている。
**``expect(1 + 2).to eq 3``**
``expect``は期待するという意味。
``to``は、であることを示す。
``eq``はイコールなので、日本語に当てはめると
``（１＋２）イコール３となることを期待します``となり、``1+2の合計は３になること``と同義になる。

この**``eq``**の部分がマッチャ（matcher）に該当し、他にも種類がある。
``to``はマッチャではなく``RSpec``のメソッド。「〜ではないこと」は``not_to(to_notでも可)``と表記できる。

よく使うマッチャを表記しておく。

```rb
・空であることを望んでいる
expect(user).to be_empty
# user.empty? が true になればパスする

・存在することを望んでいる
expect(user).to be_valid 
# user.valid? が true になればパスする

・存在しないことを望んでいる/not_to(to_not)を使わない記入法
expect(user).to be_invalid
# user.invalid? が true になればパスする
```

### テスト作成
実際に``task``のテストを記入する。

```task_spec.rb
require 'rails_helper'

RSpec.describe Task, type: :model do  
  describe 'validaton' do
    it 'タスクを問題なく作成' do
      task = build(:task)
      expect(task).to be_valid
      expect(task.errors).to be_empty
    end

    it 'タイトル空欄は無効' do
      task = build(:task,title: "")
      expect(task).to be_invalid
      expect(task.errors[:title]).to eq ["can't be blank"]
    end

    it 'ステータス空欄は無効' do
      task_without_status = build(:task,status: nil)
      expect(task_without_status).to be_invalid
      expect(task_without_status.errors[:status]).to eq ["can't be blank"]
    end

    it '同一タイトルは無効' do
      task = create(:task)
      task2 = build(:task, title: task.title)
      expect(task2).to be_invalid
      expect(task2.errors[:title]).to eq ["has already been taken"]
    end

    it '別タイトルは有効' do
      task = create(:task)
      task3 = build(:task,title: "another title")
      expect(task3).to be_valid
      expect(task3.errors).to be_empty
    end
  end
end
```

``task = create(:task)``と``task = build(:task)``の違いは、
``create``だと実際にテーブルのデータが**保存され**、
``build``ではテーブルにデータが**保存されずに**テストを実行してくれる。


## 参考記事
[RailsにRSpecを導入する方法とやさしい解説](https://qiita.com/_kanacan_/items/59d05a605ef3ed992b03)
[【Rails】factory_botの使い方メモ](https://qiita.com/at-946/items/aaff42e4a9c2e4dd58ed)
[RailsアプリへのRspecとFactory_botの導入手順](https://qiita.com/Ushinji/items/522ed01c9c14b680222c)
[rspecを読みやすくメンテしやすく書くために](https://zenn.dev/yuji_developer/articles/52cc0e356b3748)
[使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」](https://qiita.com/jnchito/items/2e79a1abe7cd8214caa5)
[FactoryBotのassociationとは](https://qiita.com/johnslith/items/c0b2a9b8ce8770e5d317)
[【FactoryBot】associationの使い方](https://qiita.com/Ryoga_aoym/items/741c57e266a9d811a2d4)