Help us understand the problem. What is going on with this article?

RSpec/FactoryBotでモデルのインスタンスメソッドを検証

More than 1 year has passed since last update.

Modelにインスタンスメソッド定義した時にちゃんとメソッドが通るか rails console で確認したりするのですが、クラスの親子関係が複雑になると rails console でインスタンス作るのが面倒になります。
そういう時はFactoryBotを使ってテスト環境にインスタンス作成してspec書いた方が楽です。

spec書いた時に気づいたtraitの使い方とこういう時にtransient使えるなっていうメモ

想定モデル(適当)

  • ReservationとMenuは一対多の関係
  • ホットペッパービューティーみたいに一つの予約に、カットとかシャンプーとかのたくさんのメニューをいれれるイメージ
Reservation.rb
class Reservation
  has_many :menus, dependent: :destroy

  validates :costomer_name, presence: true
  validates :phone_number, presence: true

  # menuの合計金額を得るメソッド
  def total_price_from_menu
    total_price = Money.new(0)
    menus.each do |menu|
      total_price += menu.price
    end
    total_price
  end 
end
Menu.rb
class Menu
  belongs_to :reservation

  validates :name, presence: true
  validates :price_cents

  # Moneyオブジェクト
  monetize :price_cents
end

FactoryBot

  • Factoryを定義する
  • traitはfactoryの特定の状態に名前をつけて自由につけ外しができるようにする。下記ではreservationをcreateした時に子要素のmenuも同時に生成するようにtraitを定義した。
  • transientは作成するデータと関係ないattributeを生成する。下記ではtransientのmenu_numの数だけ子要素のインスタンスを作成する。
spec/factories/reservations.rb
FactoryBot.define do
  factory :reservation do
    customer_name { "山田太郎" }
    phone_number { "00000000000" }
  trait :with_menus do
    transient do
      menu_num { 1 }
    end
    after(:create) do |reservation, evaluator|
      evaluator.menu_num.times do
        create(:menu, reservation: reservation)
      end
    end
  end
end
spec/factories/menus.rb
FactoryBot.define do
  factory :menus do
    association :reservation, factory: :reservation
    name { "カット" }
    price { "1000" }
  end
end

RSpec

  • テストの実行
  • with_menuで子要素が作られる
  • menu_numで引数を指定することで任意の数のインスタンスを作れる
  • インスタンスメソッドが期待どうりの挙動をするか確認する
spec/app/models/reservation_spec.rb
require 'rails_helper'

RSpec.describe Reservation, type: :model do
  describe "#total_price_from_menus" do
    let(:reservation) { create(:reservation, with_menu, menu_num: 2) }

    it "期待通りの合計金額になる" do
      expect(reservation.total_price_from_menu.format).to eq "200円"
    end
  end
end

参考: traitの切り替えやassoiationについては以下の記事が詳しい

FactoryBot(旧FactoryGirl)で関連データを同時に生成する方法いろいろ
FactoryGirlのtransientとtraitを活用する

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away