35
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Factorybotを使ったテストデータの作成方法

Posted at

Factorybotとは

Rspec標準で使える"fixture"に代わり、テストデータの準備をサポートしてくれるライブラリ。
"factory_bot_rails"はRails向けの拡張版のFactorybot

セットアップ方法

以下をGemfileに追加し、 bundle install

Gemfile
group :development, :test do
  gem 'factory_bot_rails'
end

ファイル生成

# ジェネレーターコマンドの例
$ bin/rails g factory_bot:model user

サンプルコード

spec/factories/users.rb
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

利用方法

テストデータの生成

xxxx_spec.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

35
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?