Factorybotとは
Rspec標準で使える"fixture"に代わり、テストデータの準備をサポートしてくれるライブラリ。
"factory_bot_rails"はRails向けの拡張版のFactorybot
セットアップ方法
以下をGemfileに追加し、 bundle install
group :development, :test do
gem 'factory_bot_rails'
end
ファイル生成
# ジェネレーターコマンドの例
$ bin/rails g factory_bot:model user
サンプルコード
FactoryBot.define do
factory :user, aliases: [:owner] do
first_name "Aaron"
last_name "Sumner"
sequence(:email) { |n| "tester#{n}@example.com" }
password "dottle-nouveau-pavilion-tights-furze"
end
end
# factoryの名前をモデル名とは別にしたい場合は、以下のようにクラスを指定する
FactoryBot.define do
factory :admin, class: "User"
…
ファイルの読み込み先
# デフォルトでは以下のパスのファイルを読み込む
test/factories.rb
spec/factories.rb
test/factories/*.rb
spec/factories/*.rb
利用方法
テストデータの生成
# 生成と同時に保存しない
user = FactoryBot.build(:user)
user = FactoryBot.build(:user, first_name: nil)
# 生成と同時に保存する
user = FactoryBot.create(:user)
user = FactoryBot.create(:user, first_name: "Joe")
シーケンス
データを生成するたびにn=1,2,3…と割り当てられ、異なるメールアドレスでuserを生成できる。
factory :user do
…
# シーケンシャルじゃない書き方↓
# email "tester@example.com"
# シーケンシャルな書き方↓
sequence(:email) { |n| "tester#{n}@example.com" }
end
アソシエーション
親子関係にあるクラスで子クラスのデータを生成するときに、親のデータも合わせて生成してくれる。
factory :project do
…
association :user
# 先にuserのデータを作って、
# user_id user.id等の指定がいらない
end
エイリアス
aliasesを指定しておくことで
factory :user, aliases: [:owner] do # "owner"というエイリアスを設定
…
end
指定した名前で利用できる。
factory :project do
…
association :owner # "owner"でuserのファクトリとアソシエーションできている
end
ファクトリをDRYにする
方法1:ファクトリを入れ子状にすることで、継承する。任意の属性だけ書き換えられる。
# ファクトリを継承する
FactoryBot.define do
factory :project do
sequence(:name) { |n| "Test Project #{n}" }
description "Sample project for testing purposes"
due_on 1.week.from_now
association :owner
# 昨日が締め切りのプロジェクト
factory :project_due_yesterday do
due_on 1.day.ago
end
end
end
# 呼び出し方
project = FactoryBot.create(:project_due_yesterday)
方法2:traitをつかう。任意の属性だけ書き換えられる。呼び出し方が少し変わる。
FactoryBot.define do
factory :project do
sequence(:name) { |n| "Project #{n}" }
description "A test project."
due_on 1.week.from_now
association :owner
# 昨日が締め切りのプロジェクト
trait :due_yesterday do
due_on 1.day.ago
end
end
end
# 呼び出し方
project = FactoryBot.create(:project, :due_yesterday)
使い分け方の例
私は、インスタンス自体のバリエーションは継承を、インスタンスの状況のバリエーションを表現するときはtraitを使い、生成時の引数に規則性をもたせている
- user、other_user など → 継承でDRYに
- FactoryBot.create(:user), FactoryBot.create(:other_user)
- 遅刻したuser、定刻出社のuser など→ traitでDRYに
- FactoryBot.create(:user, :late_at_work), FactoryBot.create(:user, :ontime_at_work)
コールバック
コールバック処理を使って、ファクトリの生成前後に関連処理を実施することができる。以下サンプル。
コールバックを使って関連するデータを作成する必要が あるなら、ファクトリを使うたびに呼び出されないよう、トレイトの中でセットアップすること。詳細は公式ドキュメントを参照。
# メモ付きのプロジェクト
trait :with_notes do
after(:create) { |project| create_list(:note, 5, project: project) }
end
参考文献
https://github.com/thoughtbot/factory_bot_rails
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#callbacks