テストを行うことによって、バグを発見できたり、要件通りに開発できている保証にもなるので、テストはかなり重要なものになります。
RSpecでのモデルのテスト方法を調べた中で、各記事によって方法が違っていたので、今回はFactoryBotを使用したパターンで自分なりにまとめておきます。
ではいってみましょう!
#開発環境
ruby 2.6.3
Rails 5.2.6
#前提
- FactoryBot使用
- Faker使用
- capybara使用
RSpecの準備、FactoryBotでのテストデータの定義等は、下記記事にまとめてますので、まだの方は先にこちらをどうぞ。
[Rails]RSpecでテストを行う準備(FactoryBot使用)
#モデルのテストとは
テスト項目は様々なものがあるのですが、今回はモデルのテストについて解説していきます。
モデルのテストは、そのままモデルに関するテストですw
なにかしらの投稿サイトであれば、
フォームを送信してそのデータが正常なときに、保存されるか、
バリデーションエラーが出たときに、エラーが返ってくるか、
の確認をしていきます。
空白で保存されるとエラーが出ないといけないのに、そのまま保存されるのは、十分バグとなりますので、テストを実行して確認していきます。
#モデルのテストを実行するファイルを作る
まずは、モデルに関するテストコードを書いていくファイルから作っていきます。
####①specフォルダの配下にmodelsフォルダを作成
####②spec/modelsフォルダ配下に各モデルのファイルを作っていく
今回は、
user_spec.rb
post_spec.rb
like_spec.rb
comment_spec.rb
contact_spec.rb
を作成します。
自分の開発してるアプリによって、モデルは異なるので、各自自分のものに変更してください。
####③rails_helperを読み込む
作成した各モデルのファイルで、rails_helperの設定を読み込んでいきます。
rails_helperはRSpecの設定が記述してあるファイルです。
コードを1行書くだけで、読み込めるので先に記述しておきます。
最初にまとめて書いておくことで、書き忘れを防げます!
require 'rails_helper'
#テストコードを書いていく
ではここから、実際にテストコードを書いていきます。
モデルのテストでは、正常に保存できるかと、バリデーションのエラーが返ってくるかを確認していくので、各モデルのバリデーションもあわせて確認していきます。
RSpecでのテストには、マッチャーの理解が重要
です。
まだマッチャーがなにかわからない方はこちらの記事を読んだあとに戻ってきてください。
理解が十分にできるはずです。
[Rails]RSpecでよく使うマッチャー10選
####userモデル
validates :name, presence: true, uniqueness: true, length: { maximum: 10 }
validates :email, presence: true, uniqueness: true
解説はコード内にコメントアウトで記載しています。
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'モデルのテスト' do
it "有効なuserの場合は保存されるか" do
# FactoryBotで作ったデータが有効であるか確認しています
expect(build(:user)).to be_valid
end
context "空白のバリデーションチェック" do
it "nameが空白の場合にエラーメッセージが返ってくるか" do
# userにnameカラムを空で保存したものを代入
user = build(:user, name: nil)
# バリデーションチェックを行う
user.valid?
# nameカラムでエラーが出て、エラーメッセージに"を入力してください"が含まれているか?
expect(user.errors[:name]).to include("を入力してください")
end
it "emailが空白の場合にエラーメッセージが返ってくるか" do
# emailのバリデーションチェック
user = build(:user, email: nil)
user.valid?
expect(user.errors[:email]).to include("を入力してください")
end
it "passwordが空白の場合にエラーメッセージが返ってくるか" do
# passwordのバリデーションチェック
user = build(:user, password: nil)
user.valid?
expect(user.errors[:password]).to include("を入力してください")
end
end
it "nameの文字数が11文字以上の場合エラーメッセージが返ってくるか" do
# nameカラムに"hogehogehoge"(11文字以上)の文字列を入れる
user = build(:user, name: "hogehogehoge")
user.valid?
expect(user.errors[:name]).to include("は10文字以内で入力してください")
end
context "一意性制約の確認" do
# itの前に@userにbuild(:user)を代入
before do
@user = build(:user)
end
it "同じnameの場合エラーメッセージが返ってくるか" do
# @userを保存
@user.save
# another_userにbuild(:user)を保存
another_user = build(:user)
# another_userのnameカラムに@userと同じnameを代入
another_user.name = @user.name
# @userと同じnameになるので、バリデーションチェックに引っかかる
another_user.valid?
expect(another_user.errors[:name]).to include("はすでに存在します")
end
it "同じemailの場合エラーメッセージが返ってくるか" do
# emailバージョン
@user.save
another_user = build(:user)
another_user.email = @user.email
another_user.valid?
expect(another_user.errors[:email]).to include("はすでに存在します")
end
end
end
end
####postモデル
validates :image, presence: true
validates :content, presence: true, length: { maximum: 140 }
解説はコード内にコメントアウトで記載しています。
require 'rails_helper'
RSpec.describe Post, type: :model do
describe 'モデルのテスト' do
it "有効なpostの場合は保存されるか" do
expect(build(:post)).to be_valid
end
context "空白のバリデーションチェック" do
it "imegeが空白の場合にエラーメッセージが返ってくるか" do
post = build(:post, image: nil)
post.valid?
expect(post.errors[:image]).to include("を入力してください")
end
it "contentが空白の場合にエラーメッセージが返ってくるか" do
post = build(:post, content: nil)
post.valid?
expect(post.errors[:content]).to include("を入力してください")
end
end
it "contentの文字数が141文字以上の場合エラーメッセージが返ってくるか" do
post = create(:post)
# Faker::Lorem.characters(number: 141)でランダムな文字列を141字で作成できる
post.content = Faker::Lorem.characters(number: 141)
post.valid?
expect(post.errors[:content]).to include("は140文字以内で入力してください")
end
end
end
####commentモデル
validates :comment, presence: true, length: { maximum: 50 }
解説はコード内にコメントアウトで記載しています。
require 'rails_helper'
RSpec.describe Comment, type: :model do
describe 'モデルのテスト' do
it "有効なcommentの場合は保存されるか" do
expect(build(:comment)).to be_valid
end
it "commentが空白の場合にエラーメッセージが返ってくるか" do
# commentモデルのcommentカラムです。ややこしくてすいません
comment = build(:comment, comment: nil)
comment.valid?
expect(comment.errors[:comment]).to include("を入力してください")
end
it "commentの文字数が50文字以上の場合エラーメッセージが返ってくるか" do
comment = build(:comment)
comment.comment = Faker::Lorem.characters(number: 51)
comment.valid?
expect(comment.errors[:comment]).to include("は50文字以内で入力してください")
end
end
end
####contactモデル
validates :name, presence: true, length: { maximum: 20 }
validates :email, presence: true
validates :content, presence: true, length: { minimum: 10 }
今までのコードの解説と同じになるので、省略します。
require 'rails_helper'
RSpec.describe Contact, type: :model do
describe 'モデルのテスト' do
it "有効なcontactの場合は保存されるか" do
expect(build(:contact)).to be_valid
end
context "空白のバリデーションチェック" do
it "nameが空白の場合にエラーメッセージが返ってくるか" do
contact = build(:contact, name: nil)
contact.valid?
expect(contact.errors[:name]).to include("を入力してください")
end
it "emailが空白の場合にエラーメッセージが返ってくるか" do
contact = build(:contact, email: nil)
contact.valid?
expect(contact.errors[:email]).to include("を入力してください")
end
it "contentが空白の場合にエラーメッセージが返ってくるか" do
contact = build(:contact, content: nil)
contact.valid?
expect(contact.errors[:content]).to include("を入力してください")
end
end
context "文字数のバリデーションチェック" do
it "nameの文字数が21文字以上の場合エラーメッセージが返ってくるか" do
contact = build(:contact, name: "hogehogehogehogehogehoge")
contact.valid?
expect(contact.errors[:name]).to include("は20文字以内で入力してください")
end
it "contentの文字数が10文字以下の場合エラーメッセージが返ってくるか" do
contact = build(:contact, content: "hoge")
contact.valid?
expect(contact.errors[:content]).to include("は10文字以上で入力してください")
end
end
end
end
####likeモデル
likeモデルはバリデーションなしなので、正常に保存できるかだけを確認していきます。
require 'rails_helper'
RSpec.describe Like, type: :model do
describe 'モデルのテスト' do
it "有効なuserの場合は保存されるか" do
expect(build(:like)).to be_valid
end
end
end
#テストを実行
これでモデルのテストコードがかけたので、テストを実行していきます。
ターミナルコマンドでテストを実行します。
$ bundle exec rspec spec/models
エラーが出ず、緑の文字で表示されていればテスト成功です!
#さいごに
テストコードを書いていくところでは、実際のコードを全てのせてみました。
これを参考にしてテスト実行してみてください。