序・モデルスペック

# はじめに
今回は、「参加させていただいている案件のメンバー」と「過去の自分」に向けて***「4ヶ月ほどRspecを使った人間が、意識していること」***を言語化しました。

### 対象者
- 初めて***モデルスペック***で***バリデージョンテスト***を実装する方
- モデルスペックはなんとなく知っているけど、ぼんやりしている方

なお、「モデルスペックの実装方法」の記事はたくさんあるので、詳しい内容を知りたい方はそちらをご参考ください。

### 私がいつも参考にしている文献
- [Everyday Rails - RSpecによるRailsテスト入門](https://leanpub.com/everydayrailsrspec-jp)
- [やんばるエキスパート教材](https://yanbaru-expert.com/)


# 1.モデルスペックとは
今回は、モデルスペックで***「バリデージョンをテストすること」***のみにフォーカスを当てています。

- モデルスペックとは
→　Modelに関するテストをすること

- Modelに関する***「何を」***テストするのか？
→ ***「バリデージョンがきちんと動作するか」***テストする

- バリデージョン とは
→ Modelで設定する、***不正なデータを保存しようとした時、エラーが発生する仕組み***

***個人的結論***
モデルスペックとは、自分が書いたバリデージョンが期待通りに動作しているか、***人の代わりにプログラムにチェックしてもらう仕組み***を作ること


# 2.モデルスペック実装までの流れ
私は以下の流れで実装しています。

### 2-1.テストすべき項目を洗い出す
まずは、***「私は何をテストするのか？」***を問いましょう。
ここを決めておくと、やり直しを防ぐことができ、効率よく実装できます。

例) 以下のバリデージョン の場合

```rb:user.rb

class User < ApplicationRecord
  # 空投稿を保存できない
  validates :name, presence: true
end

```

上記のバリデージョンを言葉にすると、以下のように捉えることができます。

- ***文字が空の時、エラーが発生する***

上記の内容を確かめられれば、バリデージョンは動作していることが判断できます。
これをテストすれば良いわけです。

これで、***「私は何をテストするのか？」***は明確になりました。

### 2-2.各テスト項目を枠にはめる
次に、先ほどのテスト項目を記入していきます。

```rb:user_spec.rb

# 「文字数が空の時、エラーが発生する」というテストを実装する場合

 context "文字が空の時" do
   it "エラーが発生する" do
   end
 end 

```
全て書き出していきます。



### 2-3.１個ずつテストを実装していく
あとは１つずつテストを実装していくだけです。

 
以上が***モデルスペック実装までの流れ***になります。



# 3.モデルスペックのテスト実装の流れ
では、実際にテストをしていきます。

## 3-1.準備

### 3-1-1.動作確認をする
まずは、Rspecが動作するか確認しましょう

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

```terminal:結果
❯ bundle exec rspec spec/models
No examples found.

Finished in 0.00063 seconds (files took 2.44 seconds to load)
0 examples, 0 failures  #=> テストコードがある場合は、数字か異なります。

```
結果が表示されればOKです。
エラーが発生した場合は、まずは設定をしましょう。（この記事では記載していません）

### 3-1-2.モデルスペックを実行するコマンドを知る
最低限以下のコマンドは抑えておきましょう。
私は基本的に③を使用しています。

***①全てのモデルスペック***

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

***②１つのモデルスペックのみ***

```terminal:ターミナル 
$ bundle exec rspec spec/models/ファイル名

例)
$ bundle exec rspec spec/models/user_spec.rb
```

***③特定のテストのみ***
***「いや！テストしたいのは１つだけじゃ！」***という方々に向けて。（私もその一人でした。）

①実行したいテストに`type: :xxx`を追加する（私は`doing`を入れていますが、他でも行けます）

```rb:user_spec.rb
 context "文字数が空の時"do
   it "エラーが発生する",type: :doing do
                     ^^^^^^^^^^^^^
     # 「,」を忘れないように。
     
　　end
 end 

```

②以下のコードを実行する
先ほど設定した「`type: :xxx`」を実行します。

```terminal:ターミナル 
bundle exec rspec --tag type:xxx

例）
bundle exec rspec --tag type:doing 
```

これで、`type: :xxx`をつけたテストだけ実行できます。（複数つければ複数テストできます。）


その他の記述は、こちらの記事が大変参考になります：[Rspecの便利なオプション](https://sakurawi.hateblo.jp/entry/rspec-number)


## 3-2.モデルスペック（バリデージョンテスト）の流れ
バリデージョンテストの流れは以下の通りです。良い記事はたくさんあるので、簡単に書きます。

### 3-2-1.バリデージョンにかかる（エラーが出る）テストデータを作成する
factory_bot_railsを使えば、簡単に作成できます。[factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails)
`user.rb`用のテストデータを作成する場合 → `spec/factories/users.rb`に記述する

```rb:user_spec.rb
 # 正常なテストデータの作り方
 let(:user) { build(:user) }        

 # 異常なテストデータの作り方
 let(:user) { build(:user, name: "") }
                           ^^^^^^^^
```
上記のように、バリデージョン にかかるようにテストデータを作成します。

### 3-2-2.valid?メソッドでfalseを引き出す
`validメソッド`を使用して、falseを引出します。

```rb
let(:user) { build(:user, name: "") }

user.valid?
#=> failse

# falseになると、以下の記述が可能になる。
user.errors
user.errors.messages  #=>　これを引き出す！
```


### 3-3-3.期待するエラーメッセージが含まれるか確認する

```rb

expect(user.errors.messages[:name]).to include "を入力してください"

# 「user.errors.messages[:name]」に 「"を入力してください"」が入っていることを期待する
```

今回のテストの場合

***「文字数が空の時、"を入力してください"というエラーメッセージが返ってくること」***

が確認できました。

以上が、モデルスペック（バリデージョンテスト）の流れになります。

# 4.補足


### 4-1.エイリアスを設定しておくと幸せになります
私が最近心の底から感じているのは、***「指が痛い」***です。
なので、指に疲労を溜めない為に***「長いコマンドを短い入力で実行できる設定」***をしておきます。
(ついでにタイポも激減します。)

***設定方法***
私の場合の設定です。詳しく知りたい方は、「エイリアス 設定方法」で検索下さい。

```terminal:ターミナル 
vi ~/.zshrc
```

```terminal:.zshrc
# エイリアスを追加

 # railsコマンド
 alias r='rails'
 alias rs='rails s'
 alias rdm='rails db:migrate'
 alias rdmr='rails db:migrate:reset'
 alias rds='rails db:seed'
 alias rc='rails console'
 alias bi='bundle install'
 
 # Rspec用
 # 一つだけテストする
 alias do='bundle exec rspec --tag type:doing '
 # モデルスペックをテストする
 alias m-test='bundle exec rspec spec/models'
 # リクエストスペックをテストする 
 alias r-test='bundle exec rspec spec/requests'

```

私の最近のMVPメンバーです。自分の指を楽をさせてあげるために、設定しない手はありません。

***結果***
同じ挙動になります

```
rails s  = rs
rails db:migrate = rdm
rails db:seed = rds
```
略語は上記のようでなくても問題ないので、ご自身で好きなエイリアスを設定ください。

### 4-2.うまくいかないテストがある場合
「うまくテストが実装できない」

私はよくあります。
その場合はエラーのままにしておかず、***必ずスキップ***しています。

意図的にスキップさせないと、以下の判断ができません。
***①「実装できていないテスト」***
***②「実装はできているが、テストが通っていないテスト」***


②の場合は、***「以前は通っていたテストが、今通らなくなっている状態」***に変化していることを示します。
すなわち、***「放置してはならない状態」***の為、修正が必要です。

しかし、①が混じっている状態だと②の判断ができないため、***それを調べるためのコスト***が発生します。
それを防ぐ為に、意図的にスキップさせるようにしています。

説明は長いですが、設定方法は世界一短いです。
***たった1文字追加するだけ***です。

```rb

 context "文字数が空の時" do
   xit "エラーが発生する" do    #=> 「it」から「xit」に変更
   end
 end 

```

今回の案件でPMから教えていただきました。

***「考え方」***について本当に勉強させていただいています。


### 4-3.エラーが出たら、とにかく`binding.pry`
個人的にはこれが一番良かったりします。
学びたての時に、全てに`binding.pry`を入れており、知人との勉強会でこれを披露したら、「え？そこまでする？」ってなりました。w
とにかく全てのコマンドを一つずつ丁寧に確認していきます。

```rb:user.rb

    context "name が空のとき" do
      let(:user) { build(:user) }        
      it "エラーが発生する" do

     +   binding.pry 
         #=> 「user」の確認     
         #=> 「user.valid?」を確認
 
        expect(user.valid?).to eq false       

    +    binding.pry
         #=> 「user.errors」の確認
         #=> 「user.errors.messages」の確認
         #=> 「user.errors.messages[:name]」の確認
 
        expect(user.errors.messages[:name]).to include "を入力してください"

    +   binding.pry   
        #=> 「expect(user.errors.messages[:name]).to include "を入力してください"」の確認

      end
    end

```


### 4-4.テストデータは正しく作成されているかチェックすること
個人的には、ここが一番つまづく可能性が高いです。
（特に１対多など、他のテーブルが絡む場合は苦労します）
上手くいくまで気合でググりましょう。

```terminal:確認方法

例）  let(:user) { build(:user) }        
        ^^^^^^

> user
# テストデータを呼び出せれば問題ないです。

```


# 最後に
メンバー兼半年前の自分に伝える為に作成しましたが、自分が書きたいことを書いただけのような気がします・・。
上手く伝わることを切に願います。