※2022年から技術系の記事は個人ブログに投稿しております。ぜひこちらもご覧ください→yamaday0u Blog
今回は前回の記事「RSpecの導入&関係の深いgemについて」の続きで、モデルの単体テストを行います。
前回の記事の各gemのインストール、初期設定が完了していることを前提としています。
モデルのテストファイルを作成
ターミナルでrails g rspec model モデル名
コマンドを入力してテストファイルを生成します。
$ rails g rspec model モデル名
#以下のように表示されればテストファイルの生成が成功しています。
Running via Spring preloader in process 1234
create spec/models/モデル名_spec.rb
invoke factory_bot
create spec/factories/モデル名の複数形.rb
たとえばuserモデルのテストファイルを生成するとこうなります。
$ rails g rspec:model user
Running via Spring preloader in process 1234
create spec/models/user_spec.rb
invoke factory_bot
create spec/factories/users.rb #Factory Botをインストールしておくと自動生成される
ちなみに事前にFactory Botもインストールしておくと、spec/factories/users.rb
は自動生成されます。
生成したuser_spec.rb
にはこのような記述がされています。
require 'rails_helper'
RSpec.describe User, type: :model do
pending "add some examples to (or delete) #{__FILE__}"
end
do〜end
の間にテストコードを記述していきます。
また1行目のrequire 'rails_helper
は、前回の記事で行ったrails g rspec:install
コマンドで生成したrails_helper.rb
を読み込んでいます。
FactoryBotの設定
まずは、FactoryBotでUserモデルのテストで使用するインスタンスを設定してまとめます。
FactoryBot.define do
factory :user do
name { Faker::Name }
email { Faker::Internet.free_email }
password = Faker::Internet.password(min_length: 6)
password { password }
password_confirmation { password }
end
end
passwordカラムとpassword_confirmationカラムは同じ値を入れるため、変数password
に値を代入して、各カラムに変数password
を入れます。
Faker::
の後ろに続くName
やInternet.free_email
でどんな値をランダムに生成するかを指定しています。前者なら名前、後者ならメールアドレスですね。
Fakerでは色んなタイプの値を生成してくれるので、Fakerの公式GitHubで見てみてください。
テストコードの設定
Userモデル(usersテーブル)にnameカラム、emailカラム、passwordカラムがあると想定して、
たとえば以下のようにユーザー新規登録機能のテストコードを記述します。
require 'rails_helper'
RSpec.describe User, type: :model do
before do
# 各テストコードが実行される前にFactoryBotで生成したインスタンスを@userに代入
@user = FactoryBot.build(:user)
end
# describeでテスト対象をグループ分け
describe '新規登録' do
# contextでどのような状況なのかをグループ分け
context '新規登録できるとき' do
# itで何をテストしているのかをグループ分け
it '全ての項目が入力されていれば登録できる' do
expect(@user).to be_valid
end
end
context '新規登録できないとき' do
it 'nameが空では登録できない' do
@user.name = ''
@user.valid?
expect(@user.errors.full_messages).to include("Name can't be blank")
end
it 'emailが空では登録できない' do
@user.email = ''
@user.valid?
expect(@user.errors.full_messages).to include("Email can't be blank")
end
it '重複したemailが存在する場合登録できない' do
@user.save
another_user = FactoryBot.build(:user)
another_user.email = @user.email
another_user.valid?
expect(another_user.errors.full_messages).to include('Email has already been taken')
end
it 'emailに@が含まれていないと登録できない' do
@user.email.slice!('@')
@user.valid?
expect(@user.errors.full_messages).to include('Email is invalid')
end
it 'passwordが空では登録できない' do
@user.password = ''
@user.valid?
expect(@user.errors.full_messages).to include("Password can't be blank")
end
it 'passwordが5文字以下では登録できない' do
@user.password = '123ab'
@user.valid?
expect(@user.errors.full_messages).to include('Password is too short (minimum is 6 characters)')
end
it 'passwordが存在してもpassword_confirmationが空では登録できない' do
@user.password_confirmation = ''
@user.valid?
expect(@user.errors.full_messages).to include("Password confirmation doesn't match Password")
end
end
end
end
before do ~ end
テストコードの最初のbefore do ~ end
は、describe以下の各テストコードが実行される前に処理される共通の内容を記述します。
今回の場合は登録するユーザー情報を変数@user
に代入する処理を記述しています。
これで各テストコードでユーザー情報が入った変数@user
を使えるようになりました。
describeメソッド、contextメソッド、itメソッド
テストコードのグループ分けに使用するメソッドで、
-
describe
メソッドはどのような機能なのか(テスト対象) -
context
メソッドはどのような状況なのか -
it
メソッドは何をテストしているのか
をそれぞれグループ分けしています。
expectation(エクスペクテーション)
一番最初の「describe
新規登録、context
新規登録できるとき、it
全ての項目が入力されていれば登録できる」の中にあるような以下のような記述の部分をexpectation
といい、テストで得られた結果が期待通りなのかを確認する構文です。
expect(@user).to be_valid
上記の記述のうち、be_valid
の部分をmatcher(マッチャ)
といいます。
matcher
「expectの引数(カッコの中の記述)」と「想定した挙動」が一致しているか判断します。
expectの引数にはテストで得られた実際の挙動を指定し、matcherにはどのような挙動を想定しているかを記述します
単体テストでよく使うmatcherの例
matcher | 想定する挙動 | 記述例 |
---|---|---|
include | Xの中にYという文字列が含まれているか | expect(X).to include(Y) |
be_valid | expectのインスタンスが正しく保存される | expect(X).to be_valid |
テストコードの実行
少し補足説明がなりましたが、上記のテストコードの実行をします。
ターミナルで以下のコマンドを入力します。
$ bundle exec rspec spec/models/user_spec.rb
# 実行結果
User
新規登録
新規登録できるとき
全ての項目が入力されていれば登録できる
passwordが6文字以上の半角英数字であれば登録できる
identityが空で登録できる
新規登録できないとき
nameが空では登録できない
emailが空では登録できない
重複したemailが存在する場合登録できない
emailに@が含まれていないと登録できない
passwordが空では登録できない
passwordが5文字以下では登録できない
passwordが存在してもpassword_confirmationが空では登録できない
Finished in 0.07492 seconds (files took 1.3 seconds to load)
10 examples, 0 failures
上記ではエラーなくテストが完了しましたが、expectationに記述されているmatcherのエラーメッセージなどに記述ミスがある場合は修正する必要があります。
テストコードの修正にはpry-rails
というデバッグ用のgemを使用するのですが、それは別の記事で紹介したいと思います。
RSpecのシリーズ記事
第1回:RSpecの導入&関係の深いgemについて
第2回:RSpec:モデルの単体テストの記述例 ※この記事
参考資料
RSpec: Behaviour Driven Development for Ruby
Qiita:
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」