##RSpecを使ったモデルのテスト
前回の記事にてRSpecの準備を行いました。
今回はRSpecを使ってモデルの単体テストを行っていきます。
一例として、ブログのuserモデルについてテストを行います。
このブログでは、新規ユーザーを登録する際に
- ユーザー名
- emailアドレス(重複なし)
- パスワード(6文字以上)
- パスワード確認(上のパスワードと同じ内容)
の入力が必要となります。
バリデーションの設定により、例えばユーザー名が空欄だと新規登録できません。
実際にそのように設定されているかテストを行います。
まずは基本的な部分の記述です。
require 'rails_helper'
describe User do
describe 'テスト項目' do
it "テストの内容" do
end
end
end
1行目のrequireは忘れずに記述しましょう。
rails_helper内のメソッドや設定を読み込まなくなってしまいます。
あとはdescribe内に確認したいテストの項目(どんなテストかわかれば大丈夫です)を、
it内に実際に動かすテストの内容を書いていきます。
今回のテスト名はコントローラーのアクションを意識してUser#createとしておきます。
require 'rails_helper'
describe User do
describe '#create' do
it "is invalid without name" do
user = User.new(name: "", email: "email@email.com", password: "12345678", password_confirmation: "12345678")
user.valid?
expect(user.errors[:name]).to include("can't be blank")
end
end
end
-
新しいユーザーを作成
今回のテストでは、名前が空欄だと登録できないことを確認したいのでnameは空欄、emailとpasswordは適当に入力します。passwordの確認があるので、password_confirmationにもpasswordと同じ文字列を入力します。 -
valid?メソッド
このメソッドは、バリデーションにより保存ができないかどうかを確認します。
問題なく保存できる場合はtrueを返すのみですが、バリデーションにより保存できない場合にはfalseを返すと共に、なぜ保存できないかのエラーメッセージが確認できるようになります。エラーメッセージはerrorsメソッドで呼び出します。 -
expect(〜).to include(〜)
expectで示す内容が、includeで示す値を含むかをチェックします。
ですが、文字列を指定する場合に使う""(ダブルクォーテーション)まで一致するかを確認されますので、実際には同じ文章となっているかがチェックされます。
この状態でターミナルからRSpecを起動させ、テストを実行してみます。
User
#create
is invalid without name
Finished in 0.29412 seconds (files took 1.63 seconds to load)
1 example, 0 failures
FAILEDなどの文字が出てきていなければテストは成功です。
####factory_botの活用
これでは新規ユーザーの作成がかなり長い記述になりますので、factory_botを使って簡略化させます。
FactoryBot.define do
factory :user do
name { "name" }
email { "email@email.com" }
password { "12345678" }
password_confirmation { "12345678" }
end
end
require 'rails_helper'
describe User do
describe '#create' do
it "is invalid without name" do
user = build(:user, name: nil)
user.valid?
expect(user.errors[:name]).to include("can't be blank")
end
end
end
factory_botを使うことで、ユーザーを新規作成する時のそれぞれの項目を指定しておくことができます。
複数のテストを行う中で何度もユーザーを新規作成する長ったらしい記述をする必要が無くなります。
####Fakerの活用
ちなみにFakerを使ってfactory_botの記述をすると、一例として以下のようになります。
FactoryBot.define do
password = Faker::Internet.password(min_length: 6, max_length: 8)
factory :user do
name { Faker::Internet.username(specifier: 5..6)}
email { Faker::Internet.email }
password { password }
password_confirmation { password }
end
end
Fakerを使うと、名前やメールアドレスなどのダミーデータを作成してくれます。
自動でランダムに値を生成してくれるので、テストの中で複数のユーザーを作成する場合でも名前やアドレスが重複しなくなります。
passwordは確認のため2回入力を求められますが、passwordと確認は同じ文字列でないといけないので、一度変数に代入して同じ値を使うようにしています。
password変数を2行目で指定していますが、factory :user do以下に記述しても問題ないようです。
####場合分けをして条件を網羅したテストを行う
あとは確認したい状況に応じたテストを作成していきます。
今回の例でいうと、確認したい条件は
- 名前、emailアドレス、パスワードがあれば登録できる
- 名前が空欄だと登録できない
- emailアドレスが空欄だと登録できない
- パスワードが空欄だと登録できない
- パスワードが6文字以上であれば登録できる
- パスワードが5文字以下だと登録できない
- パスワードと確認が一致していないと登録できない
- 登録済みのemailアドレスでは登録できない
このくらいでテストしてみます。
require 'rails_helper'
describe User do
describe '#create' do
it "名前、emailアドレス、パスワードがあれば登録できる" do
user = build(:user)
expect(user).to be_valid
end
it "名前が空欄だと登録できない" do
user = build(:user, name: nil)
user.valid?
expect(user.errors[:name]).to include("can't be blank")
end
it "emailアドレスが空欄だと登録できない" do
user = build(:user, email: nil)
user.valid?
expect(user.errors[:email]).to include("can't be blank")
end
it "パスワードが空欄だと登録できない" do
user = build(:user, password: nil)
user.valid?
expect(user.errors[:password]).to include("can't be blank")
end
it "パスワードが6文字以上であれば登録できる" do
password = Faker::Internet.password(min_length: 6, max_length: 6)
user = build(:user, password: password, password_confirmation: password)
expect(user).to be_valid
end
it "パスワードが5文字以下だと登録できない" do
password = Faker::Internet.password(min_length: 5, max_length: 5)
user = build(:user, password: password, password_confirmation: password)
user.valid?
expect(user.errors[:password]).to include("is too short (minimum is 6 characters)")
end
it "パスワードと確認が一致していないと登録できない" do
user = build(:user, password_confirmation: "")
user.valid?
expect(user.errors[:password_confirmation]).to include("doesn't match Password")
end
it "登録済みのemailアドレスでは登録できない" do
email = Faker::Internet.email
user = create(:user, email: email)
user2 = build(:user, email: email)
user2.valid?
expect(user2.errors[:email]).to include("has already been taken")
end
end
end
これでテストを実行してみます。
$ bundle exec rspec
下記のように表示されれば今回のテストは成功です。
User
#create
名前、emailアドレス、パスワードがあれば登録できる
名前が空欄だと登録できない
emailアドレスが空欄だと登録できない
パスワードが空欄だと登録できない
パスワードが6文字以上であれば登録できる
パスワードが5文字以下だと登録できない
パスワードと確認が一致していないと登録できない
登録済みのemailアドレスでは登録できない
Finished in 0.26533 seconds (files took 2.81 seconds to load)
8 examples, 0 failures