【Ruby on Rails】RSpecのModel(モデル)テスト書き方サンプル

スポンサーリンク

 

こんにちは、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行で書けるので、慣れるのにうってつけです。

以下をテストしてみます。

  1. 名前、メアド、パスワードがあれば有効であること
  2. 名前・メアドがなければ無効であること
  3. メアドが重複している場合は無効であること
[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

 

スポンサーリンク