#テスト
モデルのバリデーションテストについて
内容
1,Gemをインストール
2,RSpecの設定
3,テストを書く(主にバリデーション)
4,テストを実行
5,各テストの見本コード
6,もっと記述を簡単にするためのGemインストール
7,factorybotの準備
8,factory_botの記法の省略
9,終わりに
参考ページ
##1,Gemfileの下部に追加
gem 'rspec-rails'
#こちらはもともと記述があるかもしれないので
#重複しないように注意(group :development doのなかに入るようにする)
group :development do
gem 'web-console'
end
bundle install
コマンド実行
##2,RSpecの設定
Rspecは設定ファイルを作成する必要がある
$ rails g rspec:install
以下が作成される
.rspec
spec
spec/spec_helper.rb・・・「rails_helper.rbと同じくRSpec用の共通の設定を書いておくファイルですが、こちらはRSpecをRails無しで利用する際に利用します。」
spec/rails_helper.rb・・「RailsにおいてRSpecを利用する際に、共通の設定を書いておくファイルです。各テスト用ファイルでこちらのファイルを読み込むことで、共通の設定や、メソッドを適用します。」
記入する
--format documentation
##3,テストを書く
記述をするファイルはappディレクトリ以下にあるテストの対象となるコードの在り処と対応させるように作成する
※specファイルの命名規則
specファイルは対応するクラス名_spec.rbという名前
describe
1行目のdescribeは、直後のdo ~ endまでのテストのまとまりを作ります。describeの後に続く""の中にはそのまとまりの説明を書きます。
itとexample
2行目のitはexampleと呼ばれる実際に動作するテストコードのまとまりを表します。itの後に続く""の中にはそのexampleの説明を書きます。
エクスペクテーション
実際に評価される式のことです。it do ~ endの間に書きます。
expect(X).to eq Y
エクスペクテーションの文法です。xの部分に入れた式の値がYの部分の値と等しければ、テストが成功します。eqの部分を、マッチャと言います。
マッチャ
エクスペクテーションの中で、テストが成功する条件を示します。例えばeqは「等しければ」という意味になります。他にも
include(含んでいれば)、valid(バリデーションされれば)など複数のマッチャが存在します。
使えるRSpec入門・その2「使用頻度の高いマッチャを使いこなす」
rails cのやり方
$ rails c
pry > user = User.new(nickname: "", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000", first_name: "a", last_name: "a", birth_year: "2020", birth_month: "1", birth_day: "1", last_name_kana: "カナ", first_name_kana: "カナ")
pry > user.valid?
pry > user.errors
@details={:nickname=>[{:error=>:blank}]},
@messages={:nickname=>["を入力してください"]}>
##4,テストを実行
全体テストの実行
$ bundle exec rspec
単体テスト実行
#rspec 以降にフォルダとファイル名を指定する
$ bundle exec rspec spec/models/user_spec.rb
##5,各テストの見本コード
####各項目のバリデーションチェックについて
nicknameが空じゃないか確かめるコード
私は日本語化してしまっているので@message以降のあたいは日本語で入力しています。
ここを間違えると、永遠にfailuresが返ってきます
# 使われているメソッドについて共有
# valid?メソッド
# valid?メソッドを利用すると、ActiveRecord::Baseを継承しているクラスのインスタンスを保存する際に「バリデーションにより保存ができない状態であるか」を確かめることができます
# errorsメソッド
# valid?メソッドの返り値はtrue/falseですが、valid?メソッドを利用したインスタンス対してerrorsメソッドを利用すると、バリデーションにより保存ができない状態である場合なぜできないのかを確認することができます。
# includeマッチャ
# includeマッチャは、引数にとった値がexpectの引数である配列に含まれているかをチェックすることができるマッチャです。
# 今回の場合、「nicknameが空の場合はcan't be blankというエラーが出るはずだ」ということがわかっているため、include("can't be blank")のように書くことができます。
#私は日本語化してしまっているので@message以降の値("can't be blank")に相当する部分は日本語で入力しています。
#ここを間違えると、永遠にfailuresが返ってきます。。。
#rails cをして@messages={:nickname=>["を入力してください"]}>←この[]内の記述をマッチャの後の()に入れる
#実際にその通りになればこちらのエクスペクテーションはパスし、このコードは意図した動作をすると保証できます。
******************************************
# [nicknameが無い場合]
#require 'rails_helper'は必須
require 'rails_helper'
describe User do
describe '#create' do
it "nicknameないと登録できないよ" do
#ユーザークラスのインスタンスを作成してます(userモデルでバリデーションかけてるカラムを入れてください↓)
user = User.new(nickname: "", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000", first_name: "a", last_name: "a", birth_year: "2020", birth_month: "1", birth_day: "1", last_name_kana: "カナ", first_name_kana: "カナ")
#バリデーションによって保存ができないようになっているか確認
user.valid?
# バリデーションによって生じたエラー内容詳細を返す記述
# rails cで確認できる
expect(user.errors[:nickname]).to include("を入力してください")
#expect(ユーザークラスのニックネームカラム)←含まれているか(引数にとった値がexpextの引数である配列に含まれているか)
end
end
end
[結果はこんな感じでかえってきます]
User
#create
nicknameないと登録できないよ
Finished in 0.22555 seconds (files took 4.38 seconds to load)
1 example, 0 failures
##6,もっと記述を簡単にするためのGemインストール
カラムが多いと1つずつカラムを足していくのが大変なので
Gemにfactorybotをインストールして
factorybotはカラムを自動生成してくれます
group :development, :test do
#省略
gem 'rspec-rails'
gem 'factory_bot_rails'
end
bundle
します
##7,factorybotの準備
specディレクトリ直下に「factories」というディレクトリを追加し、その中に「users.rb」という名前でファイルを作成
users.rbを以下のように編集(チェックしたいからむの値を設定してます)
FactoryBot.define do
factory :user do
first_name {"kkk"}
first_name_kana {"カタカナ"}
last_name {"kkk"}
last_name_kana {"カタカナ"}
nickname {"abe"}
birth_year {2020}
birth_month {1}
birth_day {1}
email {"kkk@email.com"}
password {"00000000"}
password_confirmation {"00000000"}
end
end
これを書くとさっきの記述を下記のように置き換えられるようになります
#2つの記述は同じ意味合いを持つ
#factory_botを利用しない場合
user = User.new(nickname: "kkk", email: "kkk@gmail.com", password: "00000000", password_confirmation: "00000000", first_name: "a", last_name: "a", birth_year: "2020", birth_month: "1", birth_day: "1", last_name_kana: "カナ", first_name_kana: "カナ")
#factory_botを利用する場合(:user以降はfactories/user.rbに書いた内容になる)
user = FactoryBot.build(:user)
*buildメソッドはcreateメソッドに置き換えることもできます。
buildとほぼ同じ働きをしますが、createの場合はテスト用のDBに値が保存されます。
注意すべき点として、1回のテストが実行され、終了する毎にテスト用のDBの内容がロールバックされます。(保存された値がすべて消去されてしまう)
従って、binding.pry等でテストの実行を一時停止しないとテスト用のDBに保存された値をSequel Pro等で確認することはできません。
##8,factory_botの記法の省略
#省略
RSpec.configure do |config|
#下記の記述を追加
config.include FactoryBot::Syntax::Methods
#省略
end
セットした値の上書き
user = build(:user)
=><User:0x007fcabab94650
id: nil,
#中略
nickname: "kkk">
user = build(:user, nickname: "ttt")
=><User:0x007fcac2a88998
id: nil,
#中略
nickname: "ttt">
先ほどのspecファイルの記述をfactory_botを利用した形に変えます。
require 'rails_helper'
describe User do
describe '#create' do
# 1. nicknameとemail、passwordとpassword_confirmationなどが存在すれば登録できること
it "is valid with a nickname, email, password, password_confirmation" do
user = build(:user)
expect(user).to be_valid
end
# 2. nicknameが空では登録できないこと
it "is invalid without a nickname" do
user = build(:user, nickname: nil)
user.valid?
expect(user.errors[:nickname]).to include("を入力してください")
end
# 3. emailが空では登録できないこと
it "is invalid without a email" do
user = build(:user, email: nil)
user.valid?
expect(user.errors[:email]).to include("を入力してください")
end
# 4. first_nameが空では登録できないこと
it "is invalid without a first_name" do
user = build(:user, first_name: nil)
user.valid?
expect(user.errors[:first_name]).to include("を入力してください")
end
# 5. last_nameが空では登録できないこと
it "is invalid without a last_name" do
user = build(:user, last_name: nil)
user.valid?
expect(user.errors[:last_name]).to include("を入力してください")
end
# 6. birth_yearが空では登録できないこと
it "is invalid without a birth_year" do
user = build(:user, birth_year: nil)
user.valid?
expect(user.errors[:birth_year]).to include("を入力してください")
end
# 7. birth_yearが空では登録できないこと
it "is invalid without a birth_month" do
user = build(:user, birth_month: nil)
user.valid?
expect(user.errors[:birth_month]).to include("を入力してください")
end
# 8. birth_yearが空では登録できないこと
it "is invalid without a birth_day" do
user = build(:user, birth_day: nil)
user.valid?
expect(user.errors[:birth_day]).to include("を入力してください")
end
# 9. last_name_kanaが空では登録できないこと
it "is invalid without a last_name_kana" do
user = build(:user, last_name_kana: nil)
user.valid?
expect(user.errors[:last_name_kana]).to include("を入力してください")
end
# 10. first_name_kanaが空では登録できないこと
it "is invalid without a first_name_kana" do
user = build(:user, first_name_kana: nil)
user.valid?
expect(user.errors[:first_name_kana]).to include("を入力してください")
end
# 11. passwordが空では登録できないこと
it "is invalid without a password" do
user = build(:user, password: nil)
user.valid?
expect(user.errors[:password]).to include("を入力してください", "は7文字以上で入力してください", "は半角英数字7文字以上で入力してください")
end
# 12. passwordが存在してもpassword_confirmationが空では登録できないこと
it "is invalid without a password_confirmation although with a password" do
user = build(:user, password_confirmation: "")
user.valid?
expect(user.errors[:password_confirmation]).to include("とパスワードの入力が一致しません")
end
# 13. 重複したemailが存在する場合登録できないこと
it "is invalid with a duplicate email address" do
user = create(:user)
another_user = build(:user, email: user.email)
another_user.valid?
expect(another_user.errors[:email]).to include("はすでに存在します")
end
# 14. passwordが7文字以上であれば登録できること
it "is valid with a password that has more than 7 characters " do
user = build(:user, password: "0000000", password_confirmation: "0000000")
user.valid?
expect(user).to be_valid
end
# 15. passwordが6文字以下であれば登録できないこと
it "is invalid with a password that has less than 6 characters " do
user = build(:user, password: "000000", password_confirmation: "000000")
user.valid?
expect(user.errors[:password]).to include("は7文字以上で入力してください")
end
end
# 16. last_name_kanaがカタカナでないと登録できないこと
describe '#katakana' do
it 'last_name_kanaがカタカナで返ること' do
user = build(:user, last_name_kana: "kana")
user.valid?
expect(user.errors[:last_name_kana]).to include("はカタカナで入力してください")
end
# 17. first_name_kanaがカタカナでないと登録できないこと
it 'first_name_kanaがカタカナで返ること' do
user = build(:user, first_name_kana: "kana")
user.valid?
expect(user.errors[:first_name_kana]).to include("はカタカナで入力してください")
end
end
end
テスト結果
User
#create
is valid with a nickname, email, password, password_confirmation
is invalid without a nickname
is invalid without a email
is invalid without a first_name
is invalid without a last_name
is invalid without a birth_year
is invalid without a birth_month
is invalid without a birth_day
is invalid without a last_name_kana
is invalid without a first_name_kana
is invalid without a password
is invalid without a password_confirmation although with a password
is invalid with a duplicate email address
is valid with a password that has more than 7 characters
is invalid with a password that has less than 6 characters
#katakana
last_name_kanaがカタカナで返ること
first_name_kanaがカタカナで返ること
Finished in 0.24017 seconds (files took 2.24 seconds to load)
17 examples, 0 failures
でけた( ・∇・)
addressモデルのテスト
1/14更新
都道府県はmodelにenumで記述をしているので、今回は数字で作成しています。
もしenumなど使っていない場合は記述を変えてください。
FactoryBot.define do
factory :address do
postal_code { "111-1111" }
prefectures { 1 }
city { "豊島区" }
address { "三角町1-1-1" }
end
end
require 'rails_helper'
describe Address do
describe '#create' do
#1,prefectures,city, address, postal_codeが存在すれば登録できること
it "is valid with a prefectures,city, address, postal_code" do
address = build(:address)
expect(address).to be_valid
end
# 2. prefecturesが空では登録できないこと
it "is invalid without a prefectures" do
address = build(:address, prefectures: nil)
address.valid?
expect(address.errors[:prefectures]).to include("を入力してください")
end
# 3. cityが空では登録できないこと
it "is invalid without a city" do
address = build(:address, city: nil)
address.valid?
expect(address.errors[:city]).to include("を入力してください")
end
# 4. addressが空では登録できないこと
it "is invalid without a address" do
address = build(:address, address: nil)
address.valid?
expect(address.errors[:address]).to include("を入力してください")
end
#5. postal_codeが空では登録できないこと
it "is invalid without a postal_code" do
address = build(:address, postal_code: nil)
address.valid?
expect(address.errors[:postal_code]).to include("を入力してください")
end
# 6. postal_codeがハイフンあり7桁であれば登録できること
it "is valid with a postal_code 3桁-4桁 " do
address = build(:address, postal_code: "1234567")
address.valid?
expect(address.errors[:postal_code]).to include("はハイフンを入れて半角英数字で入力してください")
end
end
end
##9,終わりに
カタカナのテストコードに悩んですごく時間をかけてしまいましたが
そもそも勘違いが原因。
factorybotでカタカナの記述を行い、バリデーションをすでに組んでいるので
テストコードではカタカナ以外の値を入れてエラーになるかどうかを見れば良いだけでした。
一度、テストコード全て消してやり直したら案外あっさり行きました。
発想の転換て大事ですね。
でも悩みまくったおかげで面白いGemを発見したので参考までに
下記に記述残しておきます。
他のモデルのバリデーション用テストコードも順次アップします。
【Rails】まだValidatorのテストで消耗してるの?
rspec-validator_spec_helper
全角カタカナにのみマッチする正規表現
【RSpec】letはbefore内では使えない
ランダムな日本語のデータを生成するGemまとめ