はじめに
今回は、「参加させていただいている案件のメンバー」と「過去の自分」に向けて***「4ヶ月ほどRspecを使った人間が、意識していること」***を言語化しました。
対象者
- 初めてモデルスペックでバリデージョンテストを実装する方
- モデルスペックはなんとなく知っているけど、ぼんやりしている方
なお、「モデルスペックの実装方法」の記事はたくさんあるので、詳しい内容を知りたい方はそちらをご参考ください。
私がいつも参考にしている文献
1.モデルスペックとは
今回は、モデルスペックで***「バリデージョンをテストすること」***のみにフォーカスを当てています。
-
モデルスペックとは
→ Modelに関するテストをすること -
Modelに関する***「何を」***テストするのか?
→ ***「バリデージョンがきちんと動作するか」***テストする -
バリデージョン とは
→ Modelで設定する、不正なデータを保存しようとした時、エラーが発生する仕組み
個人的結論
モデルスペックとは、自分が書いたバリデージョンが期待通りに動作しているか、人の代わりにプログラムにチェックしてもらう仕組みを作ること
2.モデルスペック実装までの流れ
私は以下の流れで実装しています。
2-1.テストすべき項目を洗い出す
まずは、***「私は何をテストするのか?」***を問いましょう。
ここを決めておくと、やり直しを防ぐことができ、効率よく実装できます。
例) 以下のバリデージョン の場合
class User < ApplicationRecord
# 空投稿を保存できない
validates :name, presence: true
end
上記のバリデージョンを言葉にすると、以下のように捉えることができます。
- 文字が空の時、エラーが発生する
上記の内容を確かめられれば、バリデージョンは動作していることが判断できます。
これをテストすれば良いわけです。
これで、***「私は何をテストするのか?」***は明確になりました。
2-2.各テスト項目を枠にはめる
次に、先ほどのテスト項目を記入していきます。
# 「文字数が空の時、エラーが発生する」というテストを実装する場合
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
を入れていますが、他でも行けます)
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
に記述する
# 正常なテストデータの作り方
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
# エイリアスを追加
# 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
とにかく全てのコマンドを一つずつ丁寧に確認していきます。
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
# テストデータを呼び出せれば問題ないです。
最後に
メンバー兼半年前の自分に伝える為に作成しましたが、自分が書きたいことを書いただけのような気がします・・。
上手く伝わることを切に願います。