4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

序・モデルスペック

Last updated at Posted at 2021-03-26

はじめに

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

対象者

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

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

私がいつも参考にしている文献

1.モデルスペックとは

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

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

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

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

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

2.モデルスペック実装までの流れ

私は以下の流れで実装しています。

2-1.テストすべき項目を洗い出す

まずは、***「私は何をテストするのか?」***を問いましょう。
ここを決めておくと、やり直しを防ぐことができ、効率よく実装できます。

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

user.rb

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

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

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

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

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

2-2.各テスト項目を枠にはめる

次に、先ほどのテスト項目を記入していきます。

user_spec.rb

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

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

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

2-3.1個ずつテストを実装していく

あとは1つずつテストを実装していくだけです。

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

3.モデルスペックのテスト実装の流れ

では、実際にテストをしていきます。

3-1.準備

3-1-1.動作確認をする

まずは、Rspecが動作するか確認しましょう

ターミナル
$ bundle exec rspec spec/models
結果
❯ 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.モデルスペックを実行するコマンドを知る

最低限以下のコマンドは抑えておきましょう。
私は基本的に③を使用しています。

①全てのモデルスペック

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

②1つのモデルスペックのみ

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

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

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

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

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

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

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

例)
bundle exec rspec --tag type:doing 

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

その他の記述は、こちらの記事が大変参考になります:Rspecの便利なオプション

3-2.モデルスペック(バリデージョンテスト)の流れ

バリデージョンテストの流れは以下の通りです。良い記事はたくさんあるので、簡単に書きます。

3-2-1.バリデージョンにかかる(エラーが出る)テストデータを作成する

factory_bot_railsを使えば、簡単に作成できます。factory_bot_rails
user.rb用のテストデータを作成する場合 → spec/factories/users.rbに記述する

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

 # 異常なテストデータの作り方
 let(:user) { build(:user, name: "") }
                           ^^^^^^^^

上記のように、バリデージョン にかかるようにテストデータを作成します。

3-2-2.valid?メソッドでfalseを引き出す

validメソッドを使用して、falseを引出します。

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

user.valid?
#=> failse

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

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


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

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

今回のテストの場合

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

が確認できました。

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

4.補足

4-1.エイリアスを設定しておくと幸せになります

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

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

ターミナル
vi ~/.zshrc
.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文字追加するだけです。


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

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

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

4-3.エラーが出たら、とにかくbinding.pry

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

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.テストデータは正しく作成されているかチェックすること

個人的には、ここが一番つまづく可能性が高いです。
(特に1対多など、他のテーブルが絡む場合は苦労します)
上手くいくまで気合でググりましょう。

確認方法

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

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

最後に

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

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?