こんにちは、Railsエンジニアにょけんです。
RSpecのModelテストのサンプルコードを書いてみました。
Modelのテストでは、大雑把に分けて以下3つのテストを行います。
- バリデーション
- インスタンスメソッド
- クラスメソッド
今回は、name, email, passwordを持つUserモデルのテストを想定します。
[app/models/user.rb]
class User < ApplicationRecord # バリデーション validates :first_name, presence: true validates :last_name, presence: true validates :email, presence: true, uniqueness: true # インスタンスメソッド def fullname [first_name, last_name].join(" ") end # クラスメソッド scope :search, ->(term) { where("LOWER(message) LIKE ?", "%#{term.downcase}%")
【RSpec Modelテスト】 バリデーション
バリデーションテストはほぼ1〜2行で書けるので、慣れるのにうってつけです。
以下をテストしてみます。
- 名前、メアド、パスワードがあれば有効であること
- 名前・メアドがなければ無効であること
- メアドが重複している場合は無効であること
[app/models/user.rb]
validates :first_name, presence: true validates :last_name, presence: true validates :email, presence: true, uniqueness: true
①名前、メアド、パスワードがあれば有効であること
[spec/models/user_spec.rb]
# 名前、メアド、パスワードがあれば有効であること it "is valid with a first_name, last_name, email, and password" do @user = User.new( first_name: "John", last_name: "Brawn", email: "test@example.com", password: "i-am-john" ) expect(@user).to be_valid end
作成したインスタンスがバリデーションを通過して有効であるかどうかは、「be_valid」で確認できます。
②苗字・名前・メアドがなければ無効であること
[spec/models/user_spec.rb]
# 名前がなければ無効であること it "is invalid without a first_name" do @user = User.new(first_name: nil) expect(@user.valid?).to eq(false) end # 苗字がなければ無効であること it "is invalid without a last_name" do @user = User.new(last_name: nil) expect(@user.valid?).to eq(false) end # メアドがなければ無効であること it "is invalid without a email" do @user = User.new(email: nil) expect(@user.valid?).to eq(false) end
「can’t be blank」などのエラーメッセージが、フラッシュでちゃんと返ってくるかまで確認したい場合は、以下を参考にしてください。
[spec/models/user_spec.rb]
# 名前がなければ無効であること it "is invalid without a first_name" do @user = User.new(first_name: nil) @user.valid? expect(user.errors[:first_name]).to include("can't be blank") end # 苗字がなければ無効であること it "is invalid without a last_name" do @user = User.new(last_name: nil) @user.valid? expect(user.errors[:last_name]).to include("can't be blank") end # メアドがなければ無効であること it "is invalid without a email" do @user = User.new(email: nil) @user.valid? expect(@user.errors[:email]).to include("can't be blank") end
→first_name, last_name, emailにnilをセットして「valid?メソッド」を呼び出した場合、エラーメッセージが返ってくることを期待(expect)している。
③メアドが重複している場合は無効であること
[spec/models/user_spec.rb]
# メアドが重複している場合は無効であること it "is invalid with a duplicate email address" do User.create( first_name: "John" last_name: "Brawn", email: "test@example.com", password: "i-am-john" ) @user = User.new( first_name: "Peter", last_name: "Smith", email: "test@example.com", password: "i-am-peter" ) @user.valid? expect(@user.valid?).to eq(false) end
→emailが”test@example.com”のUserを2つ作ろうとした場合に、失敗することを確認
【RSpec Modelテスト】 インスタンスメソッド
モデル内に、first_nameとlast_nameを連結する「fullnameメソッド」があるとします。
[app/models/user.rb]
def fullname [firstname, lastname].join(' ') end
テストデータを作り、メソッドを手動で動かし、テストするイメージです。
[spec/models/user_spec.rb]
it "returns a user's full name" do @user = User.new( first_name: "John", last_name: "Brawn", email: "test@example.com" ) expect(user.fullname).to eq "John Brawn" end
比較的わかりやすいと思いますが、一点だけ注意。
RSpecで等値は、「==」ではなく「eq」を使います。
【RSpec Modelテスト】 クラスメソッド
クラスメソッド例として、渡された文字列と一致するUser名を探す「searchメソッド」があるとします。
[spec/models/user_spec.rb]
describe 'User' do describe '#search' do # 各テストの前にUserを作成 before do @user = User.create( first_name: "John", last_name: "Brawn", email: "test@example.com", password: "i-am-john" ) @other_user = User.create( first_name: "Peter", last_name: "Smith", email: "test2@example.com", password: "i-am-peter" ) end # "John"で検索した場合 context "search by 'John'" do it "returns @user" do expect(User.search("John")).to include(@user) end it "doesn't return @other_user" do expect(User.search("John")).to_not include(@other_user) end end # "Mike"で検索した場合 context "search by 'Mike'" do # 空を返す expect(User.search("Mike")).to be_empty end end end
コメント