序
factory_botはフィクスチャの代わりになるものです。 直感的な定義構文であり、複数の構築戦略(保存されたインスタンス、保存されないインスタンス、属性ハッシュ、スタブ化オブジェクト)、ファクトリ継承を含む同じクラスに対する複数のファクトリ(user、admin_user、など)に対応しています。
ドキュメントは以下に分かれています。
- 初めて使う方は、手引きから始めると良いでしょう。
- レシピには、よくある状況に対する創造的な解決策があります。
- ウィキには他のソフトウェアと統合するときの検討事項が詳しく書かれています。
- 便覧には、本プロジェクトのソフトウェアを頻繁に使う人向けの情報があります。
使用許諾
factory_bot is Copyright © 2008 Joe Ferris and thoughtbot. It is free software, and may be redistributed under the terms specified in the LICENSE file.
thoughtbotについて
factory_botはthoughtbot, inc.により保守され、資金提供されています。 thoughtbotの名前とロゴはthoughtbot, inc.の商標です。
私達はオープンソースソフトウェアが大好きです! 私達の他のプロジェクトを参照してください。 製品を設計、開発、成長させるための求人を募集しています。
構築戦略
factory_botのファクトリを定義したら、組み込みの構築戦略や独自の構築戦略を使って構築できます。
こうした戦略は全て、ActiveSupport::Notificationsを使ってfactory_bot.run_factory
計装に通知し、キー:name
、:strategy
、:traits
、:overrides
、:factory
を持つペイロードを渡します。
リストではないメソッド(.build
や.build_pair
や.create
など)は、必須の実引数であるファクトリ名を取ります。
また、省略できるトレイト名や、上塗りする属性のハッシュもあります。
最後にブロックを取れます。
このブロックは、生成されたオブジェクトを実引数とし、更新されたオブジェクトを返します。
リストのメソッド(.build_list
や.create_list
など)には2つの必須の実引数を持ちます。
ファクトリの名前と構築するインスタンスの数です。
また省略可能なトレイトと上塗りするものを取れます。
最後にブロックを取れます。
このブロックは生成されたオブジェクトとゼロ始まりの添字を実引数として取り、更新されたオブジェクトを返します。
build
FactoryBot.build
メソッドはinitialize_with
にしたがってクラスのインスタンスを構築します。
既定では.new
クラスメソッドを呼びます。
.build_list
は複数のインスタンスを構築し、.build_pair
は2つのインスタンスを構築する早道です。
initialize_with
を呼んだ後、after_build
フックを呼びます。
関連はbuild
構築戦略を使って構築されます。
create
FactoryBot.create
メソッドはinitialize_with
にしたがってクラスのインスタンスを構築し、to_create
を使って永続化します。
.create_list
クラスメソッドは複数のインスタンスを構築します。
また.create_pair
は2つのインスタンスを構築する早道です。
initialize_with
を呼んだ後、以下のフックを順番に呼びます。
after_build
before_create
- フックではない
to_create
after_create
関連はcreate
構築戦略を使って構築されます。
to_create
フックはオブジェクトの永続化方法を制御します。
オブジェクトとfactory_botの文脈を持つブロックを取り、副作用を見越して走ります。
既定では#save!
を呼びます。
attributes_for
FactoryBot.attributes_for
メソッドは、属性とその値を持つHashを、initialize_with
を使って構築します。
attributes_for_pair
メソッドとattributes_for_list
メソッドは、build_pair
とbuild_list
と似た動作です。
関連はnull
構築戦略(構築されません)を使って構築されます。
フックは呼ばれません。
build_stubbed
FactoryBot.build_stubbed
メソッドは、偽のActiveRecordオブジェクトを返します。
.build_stubbed_pair
メソッドと.build_stubbed_list
メソッドは、.build_pair
と.build_list
と似た定義です。
initialize_with
を使ってオブジェクトを構築します。
ただし、メソッドとデータを適切にスタブします。
id
は(属性で上塗りされない限り)連番で設定されます。created_at
とupdated_at
は(属性で上塗りされない限り)現在時刻に設定されます。- 全てのActiveModel::Dirtyの変更の記録が消去されます。
persisted?
は真です。new_record?
は偽です。destroyed?
は偽です。- 永続化メソッド(
#connection
や#delete
や#save
や#update
など)はRuntimeError
を投げます。
オブジェクトを設定した後after_stub
フックを呼びます。
null
FactoryBot.null
メソッドはnil
を返します。
.null_pair
メソッドはnilの対を与えます。
.null_list
は欲しい数だけnilを与えます。
内部的に使われています。
FactoryBot.find_definitions
FactoryBot.find_definitions
メソッドはプロジェクトに亙る全てのfactory_botの定義を読み込みます。
読込順はFactoryBot.definition_file_paths
属性で制御します。
既定の読込順は以下の通りです。
factories.rb
factories/**/*.rb
test/factories.rb
test/factories/**/*.rb
spec/factories.rb
spec/factories/**/*.rb
Rails
.find_definitions
メソッドは初期化後にfactory_bot_rails
により自動的に呼ばれます。
初期化時に(config/initializers
などで).definition_file_paths
を設定したり、Rails.application.config.factory_bot.definition_file_paths
で設定したりできます。
FactoryBot.define
factory_botで読み込まれる各ファイルはブロック付きのFactoryBot.define
で呼ばれることになります。
ブロックはFactoryBot::Syntax::Default::DSL
のインスタンス内で評価され、factory
やsequence
やtrait
などのメソッドを利用できます。
ファクトリ
FactoryBot.define
ブロック内ではファクトリを定義できます。
factory
を使って定義された全てのものは構築戦略を使って構築できます。
factory
メソッドは3つの実引数を取ります。
必須の名前、省略できるオプションのハッシュ、そして省略できるブロックです。
名前はSymbolにすることになっています。
オプション
:class
は、構築するクラスです。 クラスないしStringやSymbol(#to_s
に応答する任意のもの)にできます。 既定では親クラス名かファクトリ名の何れかです。:parent
はこのファクトリが継承する別のファクトリ名です。 既定ではnil
です。:aliases
はこのファクトリの別名です。 構築戦略で使えます。 既定では空リストです。:traits
はこのファクトリを構築するときに既定で使われる既定のトレイトです。 既定で空リストです。
ブロック
ブロックを使ってファクトリを定義できます。 この中では以下のメソッドが使えます。
add_attribute
association
sequence
trait
method_missing
transient
traits_for_enum
initialize_with
skip_create
to_create
before
after
callback
factory
factory
内でfactory
を使うと、暗示した親を持つ新しいファクトリを定義できます。
add_attribute
ファクトリの定義の中では、add_attribute
メソッドで、オブジェクトが構築されるときに設定されるキーバリュー対を定義できます。
add_attribute
メソッドは、名前(SymbolまたはString)とブロックの2つの実引数を取ります。
ブロックはこのオブジェクトが構築される度に呼ばれます。
ブロックは属性が構築戦略で上塗りされるときは呼ばれません。
代入ではRubyの属性セッターを呼びます。 例えば以下があるとします。
FactoryBot.define do
factory :user do
add_attribute(:name) { "Acid Burn" }
end
end
このとき#name=
セッターが使われます。
user = User.new
user.name = "Acid Burn"
早道についてはmethod_missingも参照してください。
関連
ファクトリブロック内でassociation
を使うと、このオブジェクトに隣合うオブジェクトが追加で作られます。
この名前は、ActiveRecordの文脈だと一番わかりやすいでしょう。
association
メソッドは必須の名前と省略できるオプションを取ります。
オプションは0個以上のトレイト名 (Symbol) とそれに続く属性の上塗りのハッシュです。 この関連を構築するとき、factory_botはトレイトと与えられた属性の上塗りを使います。
早道についてはmethod_missingを参照してください。 各構築戦略の関連の扱いについての説明は構築戦略を参照してください。
系列
factory_botは2つの水準の系列に対応しています。 大域的なものとファクトリ固有のものです。
大域系列
Factory.define
ブロックでは、sequence
メソッドを使うと、他のファクトリで共有できる大域系列が定義されます。
sequence
メソッドは、名前、省略できる実引数、ブロックを取ります。
名前はSymbolにすることになっています。
対応する実引数は、開始の値を表す数値(既定で1
)と:aliases
(既定で[]
)です。
開始の値は#next
に応答しなければなりません。
ブロックは値を実引数として取り、結果を返します。
系列の値は大域的に増加します。
複数の場所で:email_address
系列を使うと、都度値が漸増します。
早道についてはmethod_missingを参照してください。
ファクトリ系列
系列はファクトリブロック内に留めておけます。 構文は大域系列と同じですが、漸増する値のスコープはファクトリ定義に限られます。
加えてfactory
ブロックでsequence
を使うと、暗黙にその値にadd_attribute
を呼びます。
以下の2つは似ていますが、2つ目の例は大域系列が存在しません。
# 大域系列
sequence(:user_factory_email) { |n| "person#{n}@example.com" }
factory :user do
# 大域系列を使う
email { generate(:user_factory_email) }
end
# ファクトリのスコープの系列
factory :user do
sequence(:email) { |n| "person#{n}@example.com" }
end
トレイト
factory
定義ブロック内でtrait
メソッドを使うと、ファクトリの変更内容が名前付きで定義されます。
トレイトメソッドは名前 (Symbol) とブロックを取ります。
ブロックはfactory
定義ブロックのように扱います。
早道についてはmethod_missingを参照してください。
method_missing
factory
定義ブロックでは、add_attribute
やassociation
やsequence
やtrait
を使ってファクトリを定義できます。
また、既定のmethod_missing
定義を活かした早道も使えます。
未知のメソッド(例えばname
やadmin
やemail
やaccount
)を呼ぶと、関連や系列やトレイトやファクトリの属性に繋がります。
-
method_missingにブロックが渡されたとき、常に属性を定義します。 これにより属性に値を設定できます。
-
method_missingに実引数としてキー
:factory
を持つハッシュが渡されたとき、常に関連を定義します。 これにより関連に使うファクトリを上塗りできます。 -
同名の別のファクトリがあるとき、関連を定義します。
-
同名の大域系列があると、属性が定義されます。 系列から値が取り出されます。
-
ファクトリに同名のトレイトがあるとき、このファクトリの全ての構築に対してトレイトを変えます。
method_missing
を使ってみましょう。
以下の明示的な定義があるとします。
FactoryBot.define do
sequence(:email) { |n| "person#{n}@example.com" }
factory :account
factory :organization
factory :user, traits: [:admin] do
add_attribute(:name) { "Lord Nikon" }
add_attribute(:email) { generate(:email) }
association :account
association :org, factory: :organization
trait :admin do
add_attribute(:admin) { true }
end
end
end
上記はもっと暗黙な定義にできます。
FactoryBot.define do
sequence(:email) { |n| "person#{n}@example.com" }
factory :account
factory :organization
factory :user do
name { "Lord Nikon" } # `add_attribute`なし
admin # :traitsなし
email # `add_attribute`なし
account # `association`なし
org factory: :organization # `association`なし
trait :admin do
admin { true }
end
end
end
traits_for_enum
factory
定義ブロックには、traits_for_enum
メソッドという補助機能があります。
任意のオブジェクトに、いくつかの値のうちのどれかになる属性を持たせます。
元となった着想はActiveRecord::Enum
ですが、限られた値の集合がある任意の属性に適用できます。
このメソッドにより、それぞれの値にトレイトが作られます。
traits_for_enum
メソッドは、必須の属性名と省略できる値の集合を取ります。
値は任意のEnumerableにできます。
例えばArrayやHashです。
既定では値はnil
です。
値がArrayのとき、このメソッドは配列内の各要素にトレイトを定義します。 トレイト名は配列の要素であり、同じ配列の要素に属性を設定します。
値がHashのとき、このメソッドはキーに基づくトレイトを定義し、属性を値に設定します。 トレイト名はキーであり、属性を値に設定します。
値が何らかのEnumerableのとき、ArrayまたはHashのように扱います。
このとき、#each
がHashのように対で反復するかどうかに基づきます。
値がnilのとき、複数形になった属性名に因むクラスメソッドを使います。
FactoryBot.define do
factory :article do
traits_for_enum :visibility, [:public, :private]
# trait :public do
# visibility { :public }
# end
# trait :private do
# visibility { :private }
# end
traits_for_enum :collaborative, draft: 0, shared: 1
# trait :draft do
# collaborative { 0 }
# end
# trait :shared do
# collaborative { 1 }
# end
traits_for_enum :status
# Article.statuses.each do |key, value|
# trait key do
# status { value }
# end
# end
end
end
skip_createとto_create、そしてinitialize_with
skip_create
メソッドとto_create
メソッドとinitialize_with
メソッドは、factory_botによる構築戦略とのやり取りの仕方を制御します。
これらのメソッドはfactory
定義ブロック内で呼べます。
作用はそのファクトリに留まります。
FactoryBot.define
内にすると大域的な変更として作用します。
initialize_with
initialize_with
メソッドはブロックを取り、ファクトリのクラスのインスタンスを返します。
attributes
メソッドを使うことができます。
このメソッドはオブジェクトの全てのフィールドと値からなるハッシュです。
既定の定義は以下です。
initialize_with { new }
to_create
to_create
メソッドはFactoryBot.create
戦略を制御します。
このメソッドはinitialize_with
で構築されたオブジェクトとfactory_botの文脈を取るブロックを取ります。
文脈にはtransient
ブロックからの追加のデータがあります。
既定の定義は以下です。
to_create { |obj, context| obj.save! }
skip_create
メソッドはto_create
を何もしないように変える早道です。
これによりcreate
戦略をbuild
の同意語として使えます。
ただしcreate
フックは追加で掛かっています。
transient
factory
定義ブロック内では、クラスのインスタンスを構築することが目標です。
factory_botはこれをするものですが、文脈でのデータの記録をつけています。
データをこの文脈に設定するにはtransient
ブロックを使います。
transient
ブロックはfactory
定義ブロックのように扱います。
しかし設定した属性、関連、トレイト、系列は最終的なオブジェクトに影響しません。
これはフックやto_createとの取り合わせで一番有用です。
フック
factory
定義ブロックやFactoryBot.define
ブロック内部では、after
メソッドやbefore
メソッドやcallback
メソッドが使えます。
これにより構築戦略の一部にフックを掛けられます。
factory
定義ブロック内では、これらのコールバックはそのファクトリのみのスコープとなります。
FactoryBot.define
ブロック内では、全てのファクトリに対して大域的になります。
callback
callback
メソッドでは、任意のfactory_botのコールバックに名前でフックを掛けられます。
構築戦略の便覧に見られる通り、予め定義された名前はbefore_all
とafter_build
とbefore_create
とafter_create
とafter_stub
とafter_all
です。
このメソッドはスプラットされる名前とブロックを取ります。
名前のどれかが活性になる度にブロックが呼ばれます。
ブロックは#to_proc
に応答する任意のものにできます。
このブロックは2つの実引数を取ります。 1つはファクトリのインスタンスで、もう1つはfactory_botの文脈です。 文脈はtransient属性を持ちます。
同じコールバック名を複数回フックに掛けられます。 ブロックは全て、定義された順に実行されます。 コールバックは親から継承します。 親のコールバックがまず実行されます。
after
メソッドとbefore
メソッド
after
メソッドとbefore
メソッドはcallback
にいい感じの構文を加えています。
after(:create) do |user, context|
user.post_first_article(context.article)
end
callback(:after_create) do |user, context|
user.post_first_article(context.article)
end
FactoryBot.modify
FactoryBot.modify
クラスメソッドは使えるfactory
メソッドを上塗りしてブロックを定義します。
ブロック内で呼べる唯一のメソッドです。
このブロック内のfactory
メソッドは必須のファクトリ名とブロックを取ります。
他の全ての実引数は無視されます。
ファクトリ名は定義済みでなければなりません。
ブロックは通常のファクトリ定義ブロックです。
なおフックは消去できず、合わさったままになります。
これを使いたい状況になる理由についての詳細は、手引きを参照してください。
FactoryBot.lint
FactoryBot.lint
メソッドは各ファクトリを試して失敗したときにFactoryBot::InvalidFactoryError
を投げます。
以下の省略できる実引数を取れます。
- スプラットされるファクトリ名です。 これは1つのみにリントの対象を制限します。 既定は全てです。
:strategy
は使う構築戦略です。 既定は:create
です。:traits
は各トレイトの構築も試みるかどうかを表します。 既定はfalse
です。:verbose
はエラーでスタックトレースを表示するかどうかです。 既定はfalse
です。
.lint
をシステムにフックを掛けるお勧めの工夫については、手引きで説明されています。
FactoryBot.register_strategy
FactoryBot.register_strategy
メソッドは構築戦略の加え方です。
2つの必須の実引数である名前とクラスを取ります。
名前はSymbolで、FactoryBot::Syntax::Methods
下に現すメソッドを登録します。
クラスはメソッドassociation
とresult
を定義しなければなりません。
association
メソッドはFactoryRunner
のインスタンスを取ります。
このランナーを#run
で走らせられます。
このとき戦略名(既定は現在のもの)と省略できるブロックを渡します。
ブロックは関連が構築された後に呼ばれ、構築されたオブジェクトが渡されます。
result
メソッドは(initialize_with
を使って)このファクトリで構築されたオブジェクトを取り、この構築戦略用のファクトリの結果を返します。
準備
インストール手順は様々です。 もしあれば使っているフレームワークに依りますし、テストフレームワークについても考えられます。
インストール手順が私達の手の届かないコードに基づいて様々であるため、そうしたドキュメントはウィキで更新され続けます。 フレームワークが変わったときにウィキを編集することをお勧めします。
以降では最もよくある準備をドキュメントとして書きます。 しかし詳細はウィキをあたってください。
Gemfileの更新
Railsを使っている場合は次のようにしてください。
gem "factory_bot_rails"
Railsを使っていない場合は次のようにしてください。
gem "factory_bot"
詳細については私達のウィキを参照してください。
テストスートの構成
RSpec
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
Test::Unit
class Test::Unit::TestCase
include FactoryBot::Syntax::Methods
end
詳細については私達のウィキを参照してください。
Bundler無しで使う
Bundlerを使わないとき、gemがインストールされて呼ばれていることを確認してください。
require 'factory_bot'
一旦requireされたら、spec/factories
またはtest/factories
のディレクトリ構造があるとして、以下を走らせるだけで済みます。
FactoryBot.find_definitions
ファクトリ用の個別のディレクトリ構造を使っているとき、定義を見付けようとする前に定義ファイルパスを代えられます。
FactoryBot.definition_file_paths = %w(custom_factories_directory)
FactoryBot.find_definitions
ファクトリの個別のディレクトリがなく、行内に定義したいときも可能です。
require 'factory_bot'
FactoryBot.define do
factory :user do
name { 'John Doe' }
date_of_birth { 21.years.ago }
end
end
Railsの事前読込器とRSpec
RSpecをspring
やzeus
といったRailsの事前読込器付きで走らせるとき、関連付きのファクトリを作るときにActiveRecord::AssociationTypeMismatch
エラーに遭うかもしれません。
以下のような感じです。
FactoryBot.define do
factory :united_states, class: "Location" do
name { 'United States' }
association :location_group, factory: :north_america
end
factory :north_america, class: "LocationGroup" do
name { 'North America' }
end
end
エラーは、テストスートを走らせるときに起こります。
Failure/Error: united_states = create(:united_states)
ActiveRecord::AssociationTypeMismatch:
LocationGroup(#70251250797320) expected, got LocationGroup(#70251200725840)
2つの解決策が考えられます。
1つはテストスートを事前読込器無しで走らせることです。
もう1つはRSpecの構成にFactoryBot.reload
を追加することです。
以下のような感じです。
RSpec.configure do |config|
config.before(:suite) { FactoryBot.reload }
end
ファクトリの定義
ファクトリ名と属性
各ファクトリには名前と属性の一式があります。 名前は既定でオブジェクトのクラスを推測するために使われます。
# 以下はUserクラスを推測します。
FactoryBot.define do
factory :user do
first_name { "John" }
last_name { "Doe" }
admin { false }
end
end
クラスを明示的に指定
クラスを明示的に指定することもできます。
# 以下はUserクラスを使います(こうしなければAdminが推測されていました)
factory :admin, class: "User"
定数が使えるときは定数も渡せます(なおこれは大きいRailsアプリケーションではテストの効率性の問題を引き起こすことがあります。 定数の参照が積極読込を引き起こすためです)。
factory :access_token, class: User
定義ファイルパス
ファクトリはどこでも定義できます。
しかしファクトリが以下の場所のファイルで定義された場合、FactoryBot.find_definitions
を読んだ後に自動的に読み込まれます。
factories.rb
factories/**/*.rb
test/factories.rb
test/factories/**/*.rb
spec/factories.rb
spec/factories/**/*.rb
ハッシュ属性
Rubyのブロック構文があるため、(例えば直列化された列やJSONの列用に)属性をHash
として定義するには、2対の波括弧が必要です。
factory :program do
configuration { { auto_resolve: false, auto_define: true } }
end
代えてお好みでdo
とend
の構文にすることもできます。
factory :program do
configuration do
{ auto_resolve: false, auto_define: true }
end
end
しかし値をハッシュとして定義するとオブジェクトを構築するときにハッシュ内に値を設定するのが複雑になります。 その代わりにfactory_bot自体を使うようにしてください。
factory :program do
configuration { attributes_for(:configuration) }
end
factory :configuration do
auto_resolve { false }
auto_define { true }
end
この方法ではより簡単に構築の際に値を設定できます。
create(
:program,
configuration: attributes_for(
:configuration,
auto_resolve: true,
)
)
ベストプラクティス
お勧めは、各クラスに1つのファクトリがあるようにし、そのクラスのインスタンスを作るのに必要な、最も単純な属性一式を提供することです。 ActiveRecordオブジェクトを作る場合、これが意味するのは検証を通じて求められる属性のみを与え、既定値を持たせないということです。 他のファクトリは各クラスについて共通する筋書を押さえる上で継承を通じて作れます。
同じ名前で複数のファクトリを定義しようとするとエラーが投げられます。
静的属性
(ブロックの無い)静的属性はfactory_bot 5では使えなくなりました。 削除する決断についての詳細はこちらのブログの記事をお読みいただけます。
ファクトリを使う
構築戦略
factory_botは複数の異なる構築戦略に対応しています。
build
、create
、attributes_for
、build_stubbed
です。
# 保存されないUserインスタンスを返します。
user = build(:user)
# 保存されたUserインスタンスを返します。
user = create(:user)
# 属性のハッシュを返します。例えばUserインスタンスを構築するのに使えます。
attrs = attributes_for(:user)
# Ruby 3.0のパターン合致代入の対応と統合します。
attributes_for(:user) => {email:, name:, **attrs}
# 全ての定義された属性をスタブ化したオブジェクトを返します。
stub = build_stubbed(:user)
# 上記のどのメソッドにも、ブロックを渡せば返却されたオブジェクトがもたらされます。
create(:user) do |user|
user.posts.create(attributes_for(:post))
end
build_stubbedとMarshal.dump
なおbuild_stubbed
で作られたオブジェクトはMarshal.dump
で直列化できません。
factory_botはこれらのオブジェクトに特異メソッドを定義するからです。
複数のレコードの構築と作成
ファクトリの複数のインスタンスを一括で作成したり構築したりしたいときがあります。
built_users = build_list(:user, 25)
created_users = create_list(:user, 25)
これらのメソッドは指定量のファクトリを構築ないし作成し、配列として返します。 各ファクトリに属性を設定する上ではいつも通りハッシュで渡せます。
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
各ファクトリに異なる属性を設定するためには、これらのメソッドにブロックを渡せます。 そこでファクトリと仮引数として添字を持たせます。
twenty_somethings = build_list(:user, 10) do |user, i|
user.date_of_birth = (20 + i).years.ago
end
create_list
は保存されたインスタンスをブロックに保存します。
インスタンスを変更したらまた保存しなければなりません。
twenty_somethings = create_list(:user, 10) do |user, i|
user.date_of_birth = (20 + i).years.ago
user.save!
end
build_stubbed_list
では、完全にスタブ化されたインスタンスが得られます。
stubbed_users = build_stubbed_list(:user, 25) # スタブ化された利用者の配列
2つのレコードを一括で作る*_pair
メソッドの一式もあります。
built_users = build_pair(:user) # 2つの構築された利用者の配列
created_users = create_pair(:user) # 2つの作成された利用者の配列
複数の属性のハッシュが必要なとき、attributes_for_list
で生成します。
users_attrs = attributes_for_list(:user, 25) # 属性ハッシュの配列
属性の上塗り
使われた戦略に依らず、ハッシュを渡して定義された属性を上塗りできます。
# Userインスタンスを構築しfirst_name特性を上塗りします
user = build(:user, first_name: "Joe")
user.first_name
# => "Joe"
Ruby 3.1のHash
表記でのバリューの省略対応は属性の上塗りにぴったり馴染む機能で、変数名の繰り返しになるところを削減できます。
first_name = "Joe"
# Userインスタンスを構築しfirst_name特性を上塗りします
user = build(:user, first_name:)
user.first_name
# => "Joe"
別称
factory_botでは既存のファクトリに別称を定義して簡単に再利用できます。 この機能がしっくりくるのは、例えば、Postオブジェクトに著者の属性があり、実際にはUserクラスのインスタンスを参照しているときです。 通常factory_botは関連名からファクトリ名を推定できるものの、この場合は著者のファクトリを探して徒労になります。 そのため利用者のファクトリに別称を付ければ別称名で使えます。
factory :user, aliases: [:author, :commenter] do
first_name { "John" }
last_name { "Doe" }
date_of_birth { 18.years.ago }
end
factory :post do
# 別称を使うと以下の代わりに著者で書けます。
# association :author, factory: :user
author
title { "How to read a book effectively" }
body { "There are five steps involved." }
end
factory :comment do
# 別称を使うと以下の代わりに評論家で書けます。
# association :commenter, factory: :user
commenter
body { "Great article!" }
end
依存属性
属性は文脈を使って他の属性の値に基づくものにできます。 属性は動的な属性ブロックに処理を譲ります。
factory :user do
first_name { "Joe" }
last_name { "Blow" }
email { "#{first_name}.#{last_name}@example.com".downcase }
end
create(:user, last_name: "Doe").email
# => "joe.doe@example.com"
一過的属性
一過的属性とは、ファクトリ定義内でのみ使える属性で、オブジェクトが構築されるときには設定されません。 これにより、もっと複雑な仕組みをファクトリ内に書けます。
これらの属性はtransient
ブロック内に定義します。
factory :user do
name { "Zero Cool" }
birth_date { age&.years.ago }
transient do
age { 11 } # 上の`birth_date`に設定するためだけに使われます。
end
end
他の属性付き
他の属性から一過的属性を使えます(依存属性を参照)。
factory :user do
transient do
rockstar { true }
end
name { "John Doe#{" - Rockstar" if rockstar}" }
end
create(:user).name
#=> "John Doe - ROCKSTAR"
create(:user, rockstar: false).name
#=> "John Doe"
attributes_for付き
一過的属性はattributes_for
内では無視され、属性が存在して上塗りしようとしたとしても、モデルに設定されません。
コールバック付き
factory_botのコールバックで評価された定義自体を使う必要があるとき、(定義に)2つ目のブロック引数を宣言してそこから一過的属性を使う必要があります。 こうすると、最終的な評価された値が表されます。
factory :user do
transient do
upcased { false }
end
name { "John Doe" }
after(:create) do |user, context|
user.name.upcase! if context.upcased
end
end
create(:user).name
#=> "John Doe"
create(:user, upcased: true).name
#=> "JOHN DOE"
関連付き
一過的関連はfactory_botでは対応していません。 一過的ブロック内の関連は通常の一過的でない関連として扱われます。
必要であれば、一般には一過的属性内でファクトリを構築して回避できます。
factory :post
factory :user do
transient do
post { build(:post) }
end
end
メソッド名と予約語の属性
属性が既存のメソッドや予約語(DefinitionProxy内の全メソッド)と競合するとき、add_attribute
で定義できます。
factory :dna do
add_attribute(:sequence) { 'GATTACA' }
end
factory :payment do
add_attribute(:method) { 'paypal' }
end
継承
入れ子のファクトリ
入れ子のファクトリでは、共通する属性を繰り返すことなく、同じクラスに対して複数のファクトリを作れます。
factory :post do
title { "A title" }
factory :approved_post do
approved { true }
end
end
approved_post = create(:approved_post)
approved_post.title # => "A title"
approved_post.approved # => true
親を明示的に代入
親を明示的に代入することもできます。
factory :post do
title { "A title" }
end
factory :approved_post, parent: :post do
approved { true }
end
ベストプラクティス
前述した通り、作成に必須の属性のみを持つ各クラスの基礎的なファクトリを定義するのが良いでしょう。 それからこの基礎的な親から継承するより具体的なファクトリを作るのです。
関連
暗黙定義
ファクトリ内で関連を設定することができます。 ファクトリ名が関連名と同じとき、ファクトリ名を省けます。
factory :post do
# ...
author
end
明示定義
関連を明示的に定義できます。 属性を上塗りするときに特にしっくりくることがあります。
factory :post do
# ...
association :author
end
行内定義
通常の属性内に行内で関連を定義することもできます。
ただしattributes_for
戦略を使うときは値がnil
になることに注意してください。
factory :post do
# ...
author { association :author }
end
ファクトリの指定
異なるファクトリを指定することもできます(別称も役立つかもしれませんが)。
暗黙には以下とします。
factory :post do
# ...
author factory: :user
end
明示的には以下とします。
factory :post do
# ...
association :author, factory: :user
end
行内では以下とします。
factory :post do
# ...
author { association :user }
end
属性の上塗り
関連で属性の上塗りもできます。
暗黙には以下とします。
factory :post do
# ...
author factory: :author, last_name: "Writely"
end
明示的には以下とします。
factory :post do
# ...
association :author, last_name: "Writely"
end
もしくはファクトリの属性を使って行内でもできます。
factory :post do
# ...
author_last_name { "Writely" }
author { association :author, last_name: author_last_name }
end
関連の上塗り
属性の上塗りは紐付くオブジェクトを結び付けるのに使えます。
FactoryBot.define do
factory :author do
name { 'Taylor' }
end
factory :post do
author
end
end
eunji = build(:author, name: 'Eunji')
post = build(:post, author: eunji)
Ruby 3.1のHash
表記でのバリューの省略対応は属性の上塗りにぴったり馴染む機能で、変数名の繰り返しになるところを削減できます。
author = build(:author, name: 'Eunji')
post = build(:post, author:)
構築戦略
関連は既定で親オブジェクトと同じ構築戦略を使います。
FactoryBot.define do
factory :author
factory :post do
author
end
end
post = build(:post)
post.new_record? # => true
post.author.new_record? # => true
post = create(:post)
post.new_record? # => false
post.author.new_record? # => false
これは以前のバージョンのfactory_botの既定の挙動とは異なります。
関連の戦略は必ずしも親オブジェクトの戦略と合致していませんでした。
古い挙動を使い続けたいときは、use_parent_strategy
構成オプションをfalse
に設定できます。
FactoryBot.use_parent_strategy = false
# UserとPostを構築して保存します。
post = create(:post)
post.new_record? # => false
post.author.new_record? # => false
# Userを構築して保存します。またPostを構築するものの保存はしません。
post = build(:post)
post.new_record? # => true
post.author.new_record? # => false
紐付くオブジェクトを保存しないためには、ファクトリでstrategy: :build
を指定します。
FactoryBot.use_parent_strategy = false
factory :post do
# ...
association :author, factory: :user, strategy: :build
end
# Userを構築し、Postを構築します。ただし何れも保存しません。
post = build(:post)
post.new_record? # => true
post.author.new_record? # => true
なおstrategy: :build
オプションはassociation
の明示的な呼び出しで渡さなければなりません。
暗黙の関連では使えません。
factory :post do
# ...
author strategy: :build # <<< これはうまくいき「ません」。author_idがnilになります。
系列
大域系列
特定の形式(例えばEメールアドレス)の一意な値は系列を使って生成できます。
系列は定義ブロックでsequence
を読んで定義されます。
系列の値はgenerate
を読んで生成されます。
# 新しい系列を定義します。
FactoryBot.define do
sequence :email do |n|
"person#{n}@example.com"
end
end
generate :email
# => "person1@example.com"
generate :email
# => "person2@example.com"
動的属性付き
系列は動的属性で使えます。
FactoryBot.define do
sequence :email do |n|
"person#{n}@example.com"
end
end
factory :invite do
invitee { generate(:email) }
end
暗黙属性として
もしくは暗黙属性として以下とします。
FactoryBot.define do
sequence :email do |n|
"person#{n}@example.com"
end
end
factory :user do
email # `email { generate(:email) }`と同じです。
end
なお系列を暗黙属性として定義すると、ファクトリに系列として同じ名前があるときにうまくいかなくなります。
ファクトリ系列
また、特定のファクトリでのみ使われる系列を定義することもできます。
factory :user do
sequence(:email) { |n| "person#{n}@example.com" }
end
Ruby 2.7の連番仮引数により、行内定義はもっと更に短く書けます。
factory :user do
sequence(:email) { "person#{_1}@example.com" }
end
初期値
初期値を上塗りできます。
#next
メソッドに応答する任意の値で動作します(例えば1、2、3、「a」、「b」、「c」)。
factory :user do
sequence(:email, 1000) { |n| "person#{n}@example.com" }
end
ブロック無し
ブロック無しでは値はそれ自身を漸増させ、初期値から始めていきます。
factory :post do
sequence(:position)
end
なお系列用の値は、#next
に応答する限り、任意のEnumerableインスタンスにできます。
factory :task do
sequence :priority, %i[low medium high urgent].cycle
end
別称
系列には別称も付けられます。 系列の別称は同じ計数を共有します。
factory :user do
sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end
# :emailの計数を漸増させます。:senderと:receiverと共有しています。
generate(:sender)
別称を定義して計数に既定値 (1) を使うには以下とします。
factory :user do
sequence(:email, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end
値を設定するには以下です。
factory :user do
sequence(:email, 'a', aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end
値には#next
メソッドへの対応が必要です。
ここでの次の値は「a」で、次に「b」、などとなります。
系列URI
特定の系列を操作したい理由は沢山あります。
- 単一の値を生成:
generate
- 複数の値を生成:
generate_list
FactoryBot.rewind_sequences
で新しい値に設定- 巻き戻し:
rewind_sequence
これを達成するには、所望の系列を参照できる必要があります。 これはその系列の一意なURIで実現できます。
URI合成
各URIは3つの名前が組み合わさったものです。
位置 | 名前 | 必要かどうか |
---|---|---|
1. | ファクトリ名 | 条件付き - 系列はファクトリないしファクトリトレイト内で定義されます |
2. | トレイト名 | 条件付き - 系列はトレイト内で定義されます |
3. | 系列名 | 必ず必要 |
URIは個々のシンボルとして入力されます。
generate(:my_factory_name, :my_trait_name, :my_sequence_name)
あるいは 個々の文字列として入力されます。
generate('my_factory_name', 'my_trait_name', 'my_sequence_name')
あるいは 単一のリソース文字列として入力されます。
generate("my_factory_name/my_trait_name/my_sequence_name")
完全なURIの例
以下の例では詳しく全てのあり得るシナリオを説明しています。 コメントでは、それぞれの系列の値を生成するのに使われるURIを示しています。
FactoryBot.define do
sequence(:sequence) {|n| "global_sequence_#{n}"}
# generate(:sequence)
trait :global_trait do
sequence(:sequence) {|n| "global_trait_sequence_#{n}"}
# generate(:global_trait, :sequence)
end
factory :user do
sequence(:sequence) {|n| "user_sequence_#{n}"}
# generate(:user, :sequence)
trait :user_trait do
sequence(:sequence) {|n| "user_trait_sequence_#{n}"}
# generate(:user, :user_trait, :sequence)
end
factory :author do
sequence(:sequence) {|n| "author_sequence_#{n}"}
# generate(:author, :sequence)
trait :author_trait do
sequence(:sequence) {|n| "author_trait_sequence_#{n}"}
# generate(:author, :author_trait, :sequence)
end
end
end
end
複数URI
1つの系列に複数のURIを持たせることができます。
ファクトリやトレイトに別称があるとき、系列はそれぞれの別称、また別称の組み合わせ毎に、追加でURIを持ちます。
この例では、同じ系列を4つの異なる方法で参照できます。
factory :user, aliases: [:author] do
trait :user_trait, aliases: [:author_trait] do
sequence(:sequence) {|n| "author_trait_sequence_#{n}"}
end
end
# generate(:user, :user_trait, :sequence)
# generate(:user, :author_trait, :sequence)
# generate(:author, :user_trait, :sequence)
# generate(:author, :author_trait, :sequence)
重要
-
どれだけ深く入れ子になっていても、URIのファクトリ名の構成要素は常に、その系列が定義されているファクトリです。 決して親ファクトリではありません。
-
ファクトリが系列を継承するとき、URIはそれが定義されたファクトリを参照しなくてはなりません。 使われているファクトリではありません。
巻き戻し
系列は初めの値に巻き戻すこともできます。
全ての系列
FactoryBot.rewind_sequences
とすると、大域でファクトリである全ての系列が巻き戻ります。
FactoryBot.define do
sequence(:email) {|n| "person#{n}@example.com" }
factory :user do
sequence(:email) {|n| "user#{n}@example.com" }
end
end
generate(:email) # "person1@example.com"
generate(:email) # "person2@example.com"
generate(:email) # "person3@example.com"
generate(:user, :email) # "user1@example.com"
generate(:user, :email) # "user2@example.com"
generate(:user, :email) # "user3@example.com"
FactoryBot.rewind_sequences
generate(:email) # "person1@example.com"
generate(:user, :email) # "user1@example.com"
個々の系列
FactoryBot.rewind_sequences
に系列URIを渡すと、個々の系列を巻き戻せます。
FactoryBot.define do
sequence(:email) {|n| "global_email_#{n}@example.com" }
factory :user do
sequence(:email) {|n| "user_email_#{n}@example.com" }
end
end
FactoryBot.rewind_sequence(:email)
generate(:email)
#=> "global_email_1@example.com"
factoryBot.rewind_sequence(:user, :email)
generate(:user, :email)
#=> "user_email_1@example.com"
値の設定
コンソールで試行錯誤するとき、系列を特定の値に設定できると、大変便利です。
これはFactoryBot.set_sequence
に系列URIと新しい値を渡すと実現できます。
大域系列
大域系列は系列名と新しい値で設定されます。
FactoryBot.define do
sequence(:char, 'a') {|c| "global_character_#{c}" }
factory :user do
sequence(:name, %w[Jane Joe Josh Jayde John].to_enum)
trait :with_email do
sequence(:email) {|n| "user_#{n}@example.com" }
end
end
end
##
# 文字
generate(:char) # "global_character_a"
FactoryBot.set_sequence(:char, 'z')
generate(:char) # "global_character_z"
##
# 利用者名
generate(:user, :name) # "Jane"
FactoryBot.set_sequence(:user, :name, 'Jayde')
generate(:user, :name) # "Jayde"
##
# 利用者のEメール
generate(:user, :with_email, :email) # "user_1@example.com"
FactoryBot.set_sequence(:user, :with_email, :email, 1_234_567)
generate(:user, :with_email, :email) # "user_1234567@example.com"
補足
-
新しい値は系列の要素の集合に合致しなければなりません。 Integerベースの系列にStringを渡すことはできません!
-
整数ベースの系列、例えばレコードIDは、値として任意の正の整数を受け付けます。
-
取り得る要素が決まっている集まりの系列は、その中にある任意の値を取れます。
-
無制限の系列、例えば文字の
sequence(:unlimited,'a')
は、既定の最大の探索時間である3秒内に見つからなければ、時間切れになります。 -
時間制限を構成できます:
FactoryBot.sequence_setting_timeout = 1.5
系列の生成
直接系列を生成できるようにし、ただしオブジェクトを構築せずにおくと、テストが本当に高速になることがあります。
これは単一の値の:generate
に系列URIを渡したり、系列の値の配列に:generate_list
を渡したりして実現できます。
FactoryBot.define do
sequence(:char, 'a') {|c| "global_character_#{c}" }
factory :user do
sequence(:name, %w[Jane Joe Josh Jayde John].to_enum)
trait :with_age do
sequence(:age, 21)
end
end
end
##
# 文字
generate(:char) # "global_character_a"
generate_list(:char, 2) # ["global_character_b", "global_character_c"]
generate(:char) # "global_character_d"
##
# 利用者名
generate(:user, :name) # "Jane"
generate_list(:user, :name, 3) # ['Joe', 'Josh', 'Jayde']
generate(:user, :name) # "John"
##
# 利用者の年齢
generate(:user, :with_age, :age) # 21
generate_list(:user, :with_age, :age, 5) # [22, 23, 24, 25, 26]
generate(:user, :with_age, :age) # 27
スコープ
系列のブロックからスコープ内の属性を参照することもあるでしょう。 この場合、スコープを与えなければなりません。 そうしなければ例外が投げられます。
FactoryBot.define do
factory :user do
sequence(:email) { |n| "#{name}-#{n}@example.com" }
end
end
generate(:user, :email)
# ArgumentError, Sequence user:email failed to return a value. Perhaps it needs a scope to operate? (scope: <object>)
jester = build(:user, name: "Jester")
jester.email # "Jester-1@example.com"
generate(:user, :email, scope: jester)
# "Jester-2@example.com"
generate_list(:user, :email, 2, scope: jester)
# ["Jester-3@example.com", "Jester-4@example.com"]
テストのとき、スコープは参照されている属性に応答する任意のオブジェクトにできます。
require 'ostruct'
FactoryBot.define
factory :user do
sequence(:info) { |n| "#{name}-#{n}-#{age + n}" }
end
end
test_scope = OpenStruct.new(name: "Jester", age: 23)
generate_list('user/info', 3, scope: test_scope)
# ["Jester-1-24", "Jester-2-25", "Jester-3-26"]
一意性
一意性制約に取り組むときは、生成される系列値と競合する値を渡して上塗りしないようにご注意ください。
以下の例ではEメールが両方の利用者で同じになります。 Eメールが一意でなければならないとき、このコードはエラーになります。
factory :user do
sequence(:email) { |n| "person#{n}@example.com" }
end
FactoryBot.create(:user, email: "person1@example.com")
FactoryBot.create(:user)
トレイト
トレイトでは属性をグループに纏めて任意のファクトリに適用できます。
factory :user, aliases: [:author]
factory :story do
title { "My awesome story" }
author
trait :published do
published { true }
end
trait :unpublished do
published { false }
end
trait :week_long_publishing do
start_at { 1.week.ago }
end_at { Time.now }
end
trait :month_long_publishing do
start_at { 1.month.ago }
end_at { Time.now }
end
factory :week_long_published_story, traits: [:published, :week_long_publishing]
factory :month_long_published_story, traits: [:published, :month_long_publishing]
factory :week_long_unpublished_story, traits: [:unpublished, :week_long_publishing]
factory :month_long_unpublished_story, traits: [:unpublished, :month_long_publishing]
end
暗黙属性として
トレイトは暗黙属性として使えます。
factory :week_long_published_story_with_title, parent: :story do
published
week_long_publishing
title { "Publishing that was started at #{start_at}" }
end
なおトレイトを暗黙属性として定義すると、トレイトと同じ名前のファクトリや系列があるときに、うまくいかなくなります。
トレイトを使う
factory_botでインスタンスを構築するとき、トレイトにはSymbolのリストも渡せます。
factory :user do
name { "Friendly User" }
trait :active do
name { "John Doe" }
status { :active }
end
trait :admin do
admin { true }
end
end
# :active状態で名前が「Jon Snow」の管理者の利用者を作ります。
create(:user, :admin, :active, name: "Jon Snow")
この機能はbuild
、build_stubbed
、attributes_for
、create
で動きます。
create_list
メソッドとbuild_list
メソッドも対応しています。
2つ目の仮引数に作成・構築するインスタンス数を渡すことをご留意ください。
このファイルの「複数レコードの構築と作成」でドキュメントに書かれている通りです。
factory :user do
name { "Friendly User" }
trait :active do
name { "John Doe" }
status { :active }
end
trait :admin do
admin { true }
end
end
# 3人の管理利用者を作ります。:active状態があり、名前は「Jon Snow」です
create_list(:user, 3, :admin, :active, name: "Jon Snow")
列挙トレイト
列挙属性を持つActive Recordがあるとします。
class Task < ActiveRecord::Base
enum status: {queued: 0, started: 1, finished: 2}
end
factory_botは列挙体の取り得る値それぞれについて自動的にトレイトを定義します。
FactoryBot.define do
factory :task
end
FactoryBot.build(:task, :queued)
FactoryBot.build(:task, :started)
FactoryBot.build(:task, :finished)
手でトレイトを書くのは億劫であり、必要ありません。
FactoryBot.define do
factory :task do
trait :queued do
status { :queued }
end
trait :started do
status { :started }
end
trait :finished do
status { :finished }
end
end
end
全てのファクトリで列挙属性にトレイトを自動的に定義することが望ましくなければ、この機能をFactoryBot.automatically_define_enum_traits = false
で無効にできます。
その場合でも特定のファクトリで列挙属性にトレイトを明示的に定義できます。
FactoryBot.automatically_define_enum_traits = false
FactoryBot.define do
factory :task do
traits_for_enum(:status)
end
end
他の列挙できる値にこの機能を使うこともできます。 特別にActive Recordの列挙属性に結び付いているわけではありません。
配列の場合は以下です。
class Task
attr_accessor :status
end
FactoryBot.define do
factory :task do
traits_for_enum(:status, ["queued", "started", "finished"])
end
end
あるいはハッシュの場合は以下です。
class Task
attr_accessor :status
end
FactoryBot.define do
factory :task do
traits_for_enum(:status, { queued: 0, started: 1, finished: 2 })
end
end
属性の優先度
同じ属性を定義するトレイトはAttributeDefinitionErrorsを投げません。 最後に属性を定義したトレイトが優先されます。
factory :user do
name { "Friendly User" }
login { name }
trait :active do
name { "John Doe" }
status { :active }
login { "#{name} (active)" }
end
trait :inactive do
name { "Jane Doe" }
status { :inactive }
login { "#{name} (inactive)" }
end
trait :admin do
admin { true }
login { "admin-#{name}" }
end
factory :active_admin, traits: [:active, :admin] # loginは「admin-John Doe」になります。
factory :inactive_admin, traits: [:admin, :inactive] # loginは「Jane Doe (inactive)」になります。
end
子のファクトリの内部
子のファクトリの内部でトレイトから与えられる個々の属性を上塗りできます。
factory :user do
name { "Friendly User" }
login { name }
trait :active do
name { "John Doe" }
status { :active }
login { "#{name} (M)" }
end
factory :brandon do
active
name { "Brandon" }
end
end
ミックスインとして
トレイトはファクトリの外側で定義し、共有する属性を組み合わせるためにミックスインとして使えます。
FactoryBot.define do
trait :timestamps do
created_at { 8.days.ago }
updated_at { 4.days.ago }
end
factory :user, traits: [:timestamps] do
username { "john_doe" }
end
factory :post do
timestamps
title { "Traits rock" }
end
end
関連付き
トレイトは関連とも簡単に使えます。
factory :user do
name { "Friendly User" }
trait :admin do
admin { true }
end
end
factory :post do
association :user, :admin, name: 'John Doe'
end
# 名前が「John Doe」の管理者の利用者を作ります。
create(:post).user
ファクトリと異なる関連名を使うときは次のようにします。
factory :user do
name { "Friendly User" }
trait :admin do
admin { true }
end
end
factory :post do
association :author, :admin, factory: :user, name: 'John Doe'
# もしくは以下です。
association :author, factory: [:user, :admin], name: 'John Doe'
end
# 名前が「John Doe」の管理者の利用者を作ります。
create(:post).author
トレイト内トレイト
トレイトは他のトレイト内で使い、属性を混ぜることができます。
factory :order do
trait :completed do
completed_at { 3.days.ago }
end
trait :refunded do
completed
refunded_at { 1.day.ago }
end
end
一過的属性付き
トレイトは一過的属性を受け付けられます。
factory :invoice do
trait :with_amount do
transient do
amount { 1 }
end
after(:create) do |invoice, context|
create :line_item, invoice: invoice, amount: context.amount
end
end
end
create :invoice, :with_amount, amount: 2
コールバック
factory_botでは、6種類のコールバックを作れます。
コールバック | 時機 |
---|---|
before(:all) | 独自の戦略を含めて、どの戦略がオブジェクトの構築に使われたときも、その前に呼ばれます |
before(:build) | ファクトリが(FactoryBot.build やFactoryBot.create で)オブジェクトを構築する前に呼ばれます |
after(:build) | ファクトリが(FactoryBot.build やFactoryBot.create で)オブジェクトを構築した後に呼ばれます |
before(:create) | ファクトリが(FactoryBot.create で)オブジェクトを保存する前に呼ばれます |
after(:create) | ファクトリが(FactoryBot.create で)オブジェクトを保存した後に呼ばれます |
after(:stub) | ファクトリが(FactoryBot.build_stubbed で)オブジェクトをスタブした後に呼ばれます |
after(:all) | 独自の戦略を含めて、どの戦略が完了したときも、その後に呼ばれます |
例
構築後にオブジェクト自体のメソッドを呼ぶ
##
# 利用者ファクトリが構築された後に generate_hashed_password
# メソッドを呼ぶファクトリを定義します。
#
# なお、ブロックにはオブジェクトのインスタンスがあります。
#
factory :user do
after(:build) { |user, context| generate_hashed_password(user) }
end
オブジェクト自体の :after_create コールバックを飛ばす
##
# モデル自体の :after_create コールバックを無効にします。
# このコールバックは作成時にEメールを送るものです。
# それから作成後に再び有効にします。
#
factory :user do
before(:all){ User.skip_callback(:create, :after, :send_welcome_email) }
after(:all){ User.set_callback(:create, :after, :send_welcome_email) }
end
複数コールバック
同じファクトリに複数の種類のコールバックを定義することもできます。
factory :user do
after(:build) { |user| do_something_to(user) }
after(:create) { |user| do_something_else_to(user) }
end
ファクトリは同じ種類のコールバックをいくらでも定義することもできます。 こうしたコールバックは指定された順番に実行されます。
factory :user do
after(:create) { this_runs_first }
after(:create) { then_this }
end
create
を呼ぶとafter_build
コールバックとafter_create
コールバックの両方ともが呼ばれます。
また標準的な属性と同様に、子のファクトリは親ファクトリからコールバックを受け継ぎます(また定義もできます)。
複数のコールバックはブロックを走らせて代入できます。 (全ての戦略を通じて共有されるコールバックはないため)同じコードを様々な戦略で構築するときに有用です。
factory :user do
callback(:after_stub, :before_create) { do_something }
after(:stub, :create) { do_something_else }
before(:create, :custom) { do_a_third_thing }
end
大域コールバック
全てのファクトリにコールバックを上塗りするには、FactoryBot.define
ブロック内で定義します。
FactoryBot.define do
after(:build) { |object| puts "Built #{object}" }
after(:create) { |object| AuditLog.create(attrs: object.attributes) }
factory :user do
name { "John Doe" }
end
end
Symbol#to_proc
Symbol#to_proc
に頼るコールバックを呼べます。
# app/models/user.rb
class User < ActiveRecord::Base
def confirm!
# 利用者アカウントを確かめます。
end
end
# spec/factories.rb
FactoryBot.define do
factory :user do
after :create, &:confirm!
end
end
create(:user) # 利用者を作成して確かめます。
コールバックの順序
ファクトリの変更
ファクトリ一式が与えられているものの(gemの開発者からとしましょう)、アプリケーションにもっと合うように代えたいとき、子ファクトリを作ってそこに属性を加える代わりに、ファクトリを変更できます。
gemが以下のようにUserファクトリを与えているとします。
FactoryBot.define do
factory :user do
full_name { "John Doe" }
sequence(:username) { |n| "user#{n}" }
password { "password" }
end
end
追加の属性を加える子ファクトリを作るとすると以下になります。
FactoryBot.define do
factory :application_user, parent: :user do
full_name { "Jane Doe" }
date_of_birth { 21.years.ago }
health { 90 }
end
end
その代わりとしてファクトリを変更できます。
FactoryBot.modify do
factory :user do
full_name { "Jane Doe" }
date_of_birth { 21.years.ago }
health { 90 }
end
end
ファクトリを変更するとき、どんな属性も(コールバックを除いて)お好みで代えられます。
FactoryBot.modify
はFactoryBot.define
の外側で呼ばなければなりません。
ファクトリを違った風に操作するからです。
注意点:ファクトリのみ変更でき(系列やトレイトはできません)、コールバックは通常のものと同様に付属したままです。
そのため、変更しているファクトリでafter(:create)
コールバックを定義するとき、after(:create)
を定義しても上塗りされません。
最初のコールバックの後に実行されます。
ファクトリのリント
factory_botは既知のファクトリをリントできます。
FactoryBot.lint
FactoryBot.lint
は各ファクトリを作って作成の仮定で投げられた例外を捕えます。
作成されなかったファクトリのリスト(と対応する例外)を持つFactoryBot::InvalidFactoryError
が投げられます。
FactoryBot.lint
のお勧めの使い方はテストスートが実行される前に個別のタスクでこれを走らせることです。
before(:suite)
で走らせるとテスト単体を走らせるときにテストの効率に直に打撃があります。
Rakeタスクの例は以下です。
# lib/tasks/factory_bot.rake
namespace :factory_bot do
desc "Verify that all FactoryBot factories are valid"
task lint: :environment do
if Rails.env.test?
conn = ActiveRecord::Base.connection
conn.transaction do
FactoryBot.lint
raise ActiveRecord::Rollback
end
else
system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
fail if $?.exitstatus.nonzero?
end
end
end
FactoryBot.lint
を読んだ後、恐らくデータベースを整頓したいことでしょう。
レコードが作成されていることでしょうから。
上で与えられた例ではSQLトランザクションを使ってロールバックし、データベースが綺麗なままにします。
リントしたいファクトリのみを選んで渡してファクトリをリントできます。
factories_to_lint = FactoryBot.factories.reject do |factory|
factory.name =~ /^old_/
end
FactoryBot.lint factories_to_lint
こうするとold_
が接頭辞にない全てのファクトリがリントされます。
トレイトもリントされます。
このオプションは各ファクトリの全トレイトが有効なオブジェクトを生成することを自動で検証します。
これはlint
メソッドにtraits: true
を渡すと有効になります。
FactoryBot.lint traits: true
これは他の実引数とも組み合わせられます。
FactoryBot.lint factories_to_lint, traits: true
リントに使われる戦略を指定することもできます。
FactoryBot.lint strategy: :build
冗長なリントは各エラーに完全なバックトレースを含めます。 デバッグで役に立つことがあります。
FactoryBot.lint verbose: true
独自の構築
factory_botを使って、initialize
に渡される属性があるオブジェクトを構築したいときや、クラスを構築する上で単にnew
を呼ぶ以上のことをしたいときは、ファクトリにinitialize_with
を定義して既定の挙動を上塗りできます。
例えば以下です。
# user.rb
class User
attr_accessor :name, :email
def initialize(name)
@name = name
end
end
# factories.rb
sequence(:email) { |n| "person#{n}@example.com" }
factory :user do
name { "Jane Doe" }
email
initialize_with { new(name) }
end
build(:user).name # Jane Doe
factory_botはActiveRecordで飛び抜けて上手く書けるようになっていますが、どんなRubyクラスでも動作できます。
ActiveRecordと最大限の互換性があるよう、既定の初期化器はクラスを構築するのに実引数なしでnew
を読んで全てのインスタンスを構築します。
それから属性の書込みメソッドを呼んで全ての属性値を代入します。
ActiveRecordでは上手く動きますが、他のRubyのクラスのほとんどは実際にはうまくいきません。
以下の目的で初期化器を上塗りできます。
initialize
に実引数が必須の非ActiveRecordオブジェクトを構築する。new
ではないメソッドを使ってインスタンスをインスタンス化する。- 構築された後にインスタンスを修飾するような雑なことをする。
initialize_with
を使うとき、new
を呼ぶときにクラス自体を宣言する必要はありません。
しかし呼びたいその他のクラスメソッドは明示的にクラスに対して呼ばなければなりません。
例は以下です。
factory :user do
name { "John Doe" }
initialize_with { User.build_with_name(name) }
end
attributes
を呼んでinitialize_with
ブロック内で全ての公の属性を使うこともできます。
factory :user do
transient do
comments_count { 5 }
end
name "John Doe"
initialize_with { new(**attributes) }
end
こうするとnew
に渡される全ての属性のハッシュを構築します。
一過的属性は含まれませんが、ファクトリで定義されたその他全て(関連、評価された系列など)が渡されます。
FactoryBot.define
ブロック内に含めると全てのファクトリにinitialize_with
を定義できます。
FactoryBot.define do
initialize_with { new("Awesome first argument") }
end
initialize_with
を使うとき、initialize_with
ブロック内で使う属性は構築子でのみ代入されます。
これは以下のコードと大まかに同じです。
FactoryBot.define do
factory :user do
initialize_with { new(name) }
name { 'value' }
end
end
build(:user)
# ……とすると以下が実行されます。
User.new('value')
これは重複する代入を防止しています。 4.0より前のfactory_botでは以下が走っていました。
FactoryBot.define do
factory :user do
initialize_with { new(name) }
name { 'value' }
end
end
build(:user)
# ……とすると以下が実行されます。
user = User.new('value')
user.name = 'value'
独自の戦略
独自の構築の戦略を加えてfactory_botの挙動を拡張したいときがあります。
戦略は2つのメソッドassociation
及びresult
を定義します。
association
はFactoryBot::FactoryRunner
インスタンスを受け取ります。
このインスタンスはrun
を呼んでお好みで戦略を上塗りできます。
2つ目のメソッドresult
はFactoryBot::Evaluation
インスタンスを受け取ります。
コールバック、object
、hash
(結果のインスタンスやファクトリで定義された属性に基づくハッシュを得るためのもの)、create
のきっかけとなる手段を(notify
で)を提供します。
create
はファクトリで定義されたto_create
コールバックを実行します。
factory_botで、戦略が内部で使われる仕組みを理解するには、4つの既定の戦略それぞれのソースを眺めるのが、恐らく一番簡単です。
以下はFactoryBot::Strategy::Create
を使ってモデルにJSON表現を構築する戦略を組む例です。
class JsonStrategy
def initialize
@strategy = FactoryBot.strategy_by_name(:create).new
end
delegate :association, to: :@strategy
def result(evaluation)
@strategy.result(evaluation).to_json
end
def to_sym
:json
end
end
factory_botに新しい戦略を認識させるために、その戦略を登録できます。
FactoryBot.register_strategy(:json, JsonStrategy)
こうして呼べるようになります。
FactoryBot.json(:user)
最後に、戦略の代わりに新しいオブジェクトと登録することによってfactory_bot独自の戦略を上塗りできます。
独自コールバック
独自の戦略を使いたいとき、独自コールバックを定義できます。
class JsonStrategy
def initialize
@strategy = FactoryBot.strategy_by_name(:create).new
end
delegate :association, to: :@strategy
def result(evaluation)
result = @strategy.result(evaluation)
evaluation.notify(:before_json, result)
result.to_json.tap do |json|
evaluation.notify(:after_json, json)
evaluation.notify(:make_json_awesome, json)
end
end
def to_sym
:json
end
end
FactoryBot.register_strategy(:json, JsonStrategy)
FactoryBot.define do
factory :user do
before(:json) { |user| do_something_to(user) }
after(:json) { |user_json| do_something_to(user_json) }
callback(:make_json_awesome) { |user_json| do_something_to(user_json) }
end
end
オブジェクトを永続化するための独自メソッド
既定で、レコードを作成するとインスタンスにsave!
を呼びます。
これは常に最適ではないことがあるため、ファクトリにto_create
を定義して挙動を上塗りできます。
factory :different_orm_model do
to_create { |instance| instance.persist! }
end
作成で永続化のメソッドも一緒に無効化するには、ファクトリでskip_create
することができます。
factory :user_without_database do
skip_create
end
全てのファクトリでto_create
を上塗りするにはFactoryBot.define
ブロック内で定義します。
FactoryBot.define do
to_create { |instance| instance.persist! }
factory :user do
name { "John Doe" }
end
end
ActiveSupportの計装
何のファクトリが作られたか(またどの構築戦略か)把握する目的で、コンパイルされて走るファクトリを購読する方法を提供するためにActiveSupport::Notifications
がincludeされています。
一例としては実行時間の閾値に基づいてファクトリを把握することです。
ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload|
execution_time_in_seconds = finish - start
if execution_time_in_seconds >= 0.5
$stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}"
end
end
別の例としては全てのファクトリを対象にテストスートを通じてどのように使われたかを追跡するものです。
RSpecを使っているとき、before(:suite)
とafter(:suite)
を加えるだけです。
factory_bot_results = {}
config.before(:suite) do
ActiveSupport::Notifications.subscribe("factory_bot.run_factory") do |name, start, finish, id, payload|
factory_name = payload[:name]
strategy_name = payload[:strategy]
factory_bot_results[factory_name] ||= {}
factory_bot_results[factory_name][strategy_name] ||= 0
factory_bot_results[factory_name][strategy_name] += 1
end
end
config.after(:suite) do
puts factory_bot_results
end
別の例として、ファクトリが一緒にコンパイルされる属性とトレイトを追跡することが関係します。
RSpecを使っているとき、before(:suite)
及びafter(:suite)
ブロックを加えてfactory_bot.compile_factory
の通知を購読できます。
factory_bot_results = {}
config.before(:suite) do
ActiveSupport::Notifications.subscribe("factory_bot.compile_factory") do |name, start, finish, id, payload|
factory_name = payload[:name]
factory_class = payload[:class]
attributes = payload[:attributes]
traits = payload[:traits]
factory_bot_results[factory_class] ||= {}
factory_bot_results[factory_class][factory_name] = {
attributes: attributes.map(&:name)
traits: traits.map(&:name)
}
end
end
config.after(:suite) do
puts factory_bot_results
end
has_many関連
has_many
の関係でデータを生成するにはいくつか方法があります。
最も単純な方法は素のRubyで補助メソッドを書いて異なるレコードと結び付けることです。
FactoryBot.define do
factory :post do
title { "Through the Looking Glass" }
user
end
factory :user do
name { "Rachel Sanchez" }
end
end
def user_with_posts(posts_count: 5)
FactoryBot.create(:user) do |user|
FactoryBot.create_list(:post, posts_count, user: user)
end
end
create(:user).posts.length # 0
user_with_posts.posts.length # 5
user_with_posts(posts_count: 15).posts.length # 15
オブジェクトの作成を完全にfactory_botに留める方が好みであれば、after(:create)
コールバック内で記事を構築できます。
FactoryBot.define do
factory :post do
title { "Through the Looking Glass" }
user
end
factory :user do
name { "John Doe" }
# user_with_postsは利用者が作成された後に記事データを作ります。
factory :user_with_posts do
# posts_countは文脈を介してコールバックで使える一過的属性として宣言されています。
transient do
posts_count { 5 }
end
# after(:create) は利用者インスタンス自体と文脈の2つの値を譲渡します。
# この文脈には、一過的属性を含むファクトリ由来の全ての値が保管されています。
# `create_list`の2つ目の実引数は作成されるレコードの数であり、利用者が適切に記事に紐付いていることを確かめています。
after(:create) do |user, context|
create_list(:post, context.posts_count, user: user)
# ここでレコードを再読込する必要があるかもしれません。
# アプリケーションに依ります。
user.reload
end
end
end
end
create(:user).posts.length # 0
create(:user_with_posts).posts.length # 5
create(:user_with_posts, posts_count: 15).posts.length # 15
以下はデータベースへの保存せずに済ませる単純な例です。
build
やbuild_stubbed
やcreate
で機能します(ただしattributes_for
では上手くいきません)。
行内関連が使えます。
FactoryBot.define do
factory :post do
title { "Through the Looking Glass" }
user
end
factory :user do
name { "Taylor Kim" }
factory :user_with_posts do
posts { [association(:post)] }
end
# または
trait :with_posts do
posts { [association(:post)] }
end
end
end
create(:user).posts.length # 0
create(:user_with_posts).posts.length # 1
build(:user_with_posts).posts.length # 1
build_stubbed(:user_with_posts).posts.length # 1
柔軟性のため、これをコールバックの例のposts_count
一過的属性と組み合わせられます。
FactoryBot.define do
factory :post do
title { "Through the Looking Glass" }
user
end
factory :user do
name { "Adiza Kumato" }
factory :user_with_posts do
transient do
posts_count { 5 }
end
posts do
Array.new(posts_count) { association(:post) }
end
end
end
end
create(:user_with_posts).posts.length # 5
create(:user_with_posts, posts_count: 15).posts.length # 15
build(:user_with_posts, posts_count: 15).posts.length # 15
build_stubbed(:user_with_posts, posts_count: 15).posts.length # 15
has_and_belongs_to_many関連
has_and_belongs_to_many
の関係でデータを生成するのは前述のhas_many
の関係とよく似ています。
属性名の単数形のバージョンへの単一のオブジェクトではなく、モデルの複数形の属性名にオブジェクトの配列を渡す必要があります。
def profile_with_languages(languages_count: 2)
FactoryBot.create(:profile) do |profile|
FactoryBot.create_list(:language, languages_count, profiles: [profile])
end
end
もしくはコールバックの方法では以下となります。
factory :profile_with_languages do
transient do
languages_count { 2 }
end
after(:create) do |profile, context|
create_list(:language, context.languages_count, profiles: [profile])
profile.reload
end
end
あるいは行内関連の方法では以下です(なおここでのinstance
の利用は、構築されるプロファイルへの参照です)。
factory :profile_with_languages do
transient do
languages_count { 2 }
end
languages do
Array.new(languages_count) do
association(:language, profiles: [instance])
end
end
end
多相関連
多相関連はトレイトで制御できます。
FactoryBot.define do
factory :video
factory :photo
factory :comment do
for_photo # 何も指定されなければ既定で:for_photoトレイトです。
trait :for_video do
association :commentable, factory: :video
end
trait :for_photo do
association :commentable, factory: :photo
end
end
end
こうして以下とできます。
create(:comment)
create(:comment, :for_video)
create(:comment, :for_photo)
相互接続関連
オブジェクトが相互接続する方法は際限なくあり、factory_botは常にそうした関係を扱うのに向いていないかもしれません。 factory_botを使って個々のオブジェクトを構築し、素のRubyで補助メソッドを書いてこうしたオブジェクトを結び付けるのが理に適っている場合があります。
つまり、より複雑で相互接続された関係は、行内関連と構築されるinstance
への参照とを使ってfactory_botで構築できるということです。
モデルが以下のようなものであるとします。
ここで紐付くStudent
とProfile
は両方とも同じSchool
に属します。
class Student < ApplicationRecord
belongs_to :school
has_one :profile
end
class Profile < ApplicationRecord
belongs_to :school
belongs_to :student
end
class School < ApplicationRecord
has_many :students
has_many :profiles
end
生徒とプロファイルが相互に接続し、同じ学校にあることを以下のようなファクトリで確かめられます。
FactoryBot.define do
factory :student do
school
profile { association :profile, student: instance, school: school }
end
factory :profile do
school
student { association :student, profile: instance, school: school }
end
factory :school
end
なおこの方法はbuild
やbuild_stubbed
やcreate
で上手くいきます。
ただしattributes_for
を使うと紐付けからnil
が返ります。
また、独自のinitialize_with
内で属性を代入したとき(例えばinitialize_with { new(**attributes) }
)、これらの属性はinstance
を参照すべきではないことにも注意してください。
nil
になるからです。
factory_bot

factory_botはフィクスチャの代わりになるものです。 直感的な定義構文であり、複数の構築戦略(保存されたインスタンス、保存されないインスタンス、属性ハッシュ、スタブ化オブジェクト)、ファクトリ継承を含む同じクラスに対する複数のファクトリ(user、admin_user、など)に対応しています。
Railsでfactory_botを使いたいときはfactory_bot_railsを参照してください。
factory_girlから移行されますか
手引きをご確認ください。
ドキュメント
factory_botの本で網羅的な参照、手引き、レシピを参照してください。
RSpecやRailsといったサードパーティライブラリの統合についての情報はfactory_botのウィキを参照してください。
詳細な導入のための映像もあります。 Upcaseで無料で見られます。
インストール
以下を走らせてください。
bundle add factory_bot
シェルで手動でgemをインストールするには、以下を走らせてください。
gem install factory_bot
対応しているRubyのバージョン
対応しているRubyのバージョンは.github/workflows/build.yml
に一覧になっています。
その他の情報
有用なツール
- FactoryTraceは使われていないファクトリやトレイトを見付けるのに役立ちます。
- ruby-lsp-factory_bot /
ruby-lsp-rails-factory-bot
- ruby-lspとの統合によるインテリセンスの提供
貢献
CONTRIBUTING.mdを参照してください。
factory_botは元はJoe Ferrisにより書かれ、thoughtbotにより保守されています。 多くの向上と不具合の修正がオープンソースコミュニティにより貢献されました。
使用許諾
factory_bot is Copyright © 2008 Joe Ferris and thoughtbot. It is free software, and may be redistributed under the terms specified in the LICENSE file.
thoughtbotについて
このリポジトリはthoughtbot, inc.により保守され、資金提供されています。 thoughtbotの名前とロゴはthoughtbot, inc.の商標です。
私達はオープンソースソフトウェアが大好きです! 私達の他のプロジェクトを参照してください。 求人を募集しています。
お知らせ
6.5.4(2025年6月13日)
- ファクトリ内の
method_missing
を通じてdefinition
という名前の利用者が定義したメソッドを設定できない不具合を修正しました。 (CodeMeister)
6.5.3(2025年6月2日)
- 修正:ブロックのないファクトリ系列 (CodeMeister)
- 追加:系列の設定、生成、巻き戻しのメソッドを新規追加 (CodeMeister)
6.5.2(2025年5月20日)
* 変更:「冗長なリント」のテストを更新し、Ruby 3.4で変更されたバックトレースを受け付けるようにしました (CodeMeister)
* 修正:`build_stubbed`で`created_at`と`updated_at`に同じ時間記録を設定するようにしました (Kim
Emmanuel)
* 修正:系列を改修し、複製されたトレイトが親系列を確実に使うようにしました。 (CodeMeister)
* 文書:definition_file_paths commentを修正 (Milo Winningham)
* 文書:README.mdの有用なツールの節にruby-lsp拡張を追加 (johansenja)
* 文書:定義ファイルのパスについての文書を修正 (Ryo Nakamura)
* 文書:has_many-associations.mdを更新し、トレイトで行内関連を使えることに言及しました (Matthew
Zagaja)
* 文書:「Factory Girlからの移行」の手引きのリンクを修正 (Neil Carvalho)
6.5.1(2025年1月31日)
- 変更:可能な場合、ActiveRecordのトランザクション中にリント検査を実行 (Sean Doyle)
- 修正:コンパイル時間を計測するときに無作為にテストが失敗していた点 (CodeMeinster)
- 修正:activesupportの最小要件バージョンを6.1に引き上げました (Earlopain)
- 内部的:開発依存関係を更新 (Neil Carvalho)
6.5.0(2024年9月6日)
- @elasticspoonにより、 https://github.com/thoughtbot/factory_bot/pull/1623 でref/factory.mdの壊れていたリンクが修正されました。 関連するイシューは1621です
- @ydah により、 https://github.com/thoughtbot/factory_bot/pull/1625 に標準設定が追加されました
- @stefannibrasilにより、 https://github.com/thoughtbot/factory_bot/pull/1628 でdynamic-readmeという、再利用のためのワークフローを呼ぶようにしました
- @stefannibrasilにより、 https://github.com/thoughtbot/factory_bot/pull/1630 で再びreadmeが更新されました
- @stefannibrasilによる https://github.com/thoughtbot/factory_bot/pull/1635 で、READMEが更新されたときにのみ、ワークフローを走らせるようにしました
- 自動生成:@github-actionsによる https://github.com/thoughtbot/factory_bot/pull/1637 で、READMEのDynamic Sectionを更新しました
- @m-gizziによる https://github.com/thoughtbot/factory_bot/pull/1642 で、文字列として渡された、下線文字のあるクラス名を扱う場合を、build_classに追加しました
- @berkosにより、 https://github.com/thoughtbot/factory_bot/pull/1615 でRuby 3.3がCIに追加されました
- @smabosheにより、 https://github.com/thoughtbot/factory_bot/pull/1655 でDependabotの構成が更新されました
- @sarahraqueldにより、 https://github.com/thoughtbot/factory_bot/pull/1651 で新しいメンテナがCODEOWNERSに追加されました
- @sarahraqueldによる https://github.com/thoughtbot/factory_bot/pull/1666 で、文書の書式とファイル名の競合を改善しました
- @sarahraqueldによる https://github.com/thoughtbot/factory_bot/pull/1677 で、動的なセキュリティワークフローと SECURITY.md ファイルを追加しました
- 自動生成:@github-actionsによる https://github.com/thoughtbot/factory_bot/pull/1678 で、SECURITY中の動的な節を更新しました
- @Earlopainにより、 https://github.com/thoughtbot/factory_bot/pull/1686 でrails 7.2の互換性が確保されました
- @ddieulivolにより、 https://github.com/thoughtbot/factory_bot/pull/1688 でトレイトの文書にあるファクトリ定義が修正されました
6.4.6(2023年1月30日)
- 修正:gemspecのRubyの最小バージョンの要件を上げました (Earlopain)。
- 修正:
FactoryBot.modify
のリンク切れを直しました (Matt Brictson)。 - 修正:
FactoryBot.lint
のリンク切れを直しました (Anton Dieterle)。
6.4.5(2023年12月29日)
- 変更:Ruby 3.0以降、Rails6.1以降に対応しました (Mike Burns)。
6.4.4(2023年12月27日)
- 内部的:observerの依存関係を除きました (Earlopain)。
6.4.3(2023年12月26日)
- 修正:build_stubbedでIDセッターのないモデルに対応しました (Olivier Bellone)。
- 修正:明示的な観察器の依存 (Oleg Antonyan)。
- 内部:Rails 7.1をCIに追加 (Olivier Bellone)。
- 内部:githubのactions/checkoutをv4に更新 (Lorenzo Zabot)。
- 内部:CIでdisable-error_highlightを渡すのを中止 (Mike Burns)。
- 内部:例外伝文検査を緩和 (Mike Burns)。
6.4.2(2023年11月22日)
- 修正:最上水準のトレイトで、クラスがActiveSupport::Notificationsに渡されるようにしました (makicamel)。
6.4.1(2023年11月20日)
- 修正:トレイトのあるファクトリについて、そのクラスがActiveSupport::Notificationsに渡されるようになりました (makicamel)。
6.4.0 (November 17, 2023)
- Added: if
build_stubbed
detects a UUID primary key, generate the correct type (Peter Boling, Alexandre Ruban). - Docs: show examples of Ruby 3 syntactic sugars (Sean Doyle).
- Internal: resolve test warning messages (Mike Burns).
6.3.0 (September 1, 2023)
- Fix: link to changelog for RubyGems (Berkan Ünal).
- Fix: integrate with Ruby 3.2's
did_you_mean
library (Daniel Colson). - Changed: explicitly define
#destroyed?
within theStub
strategy to returnfalse
to be consistent with ActiveRecord (Benjamin Fleischer). - Added: announce
factory_bot.compile_factory
notification (Sean Doyle). - Docs: clarify that custom strategies need to define
#to_sym
(Edmund Korley, Jonas S). - Docs: fix CI link in README (Mark Huk).
- Docs: fix GitHub links (Robert Fletcher).
- Docs: install this library with
bundle add
(Glauco Custódio). - Docs: re-write into mdBook (Mike Burns, Sara Jackson, Stefanni Brasil)
- Docs: clarify that automatic trait definitions could introduce new linting errors (Lawrence Chou).
- Internal: skip TruffleRuby on Rails 5.0, 5.1, 5.2 (Andrii Konchyn).
- Internal: fix typoes throughout codebase (Yudai Takada).
- Internal: run CI on
actions/checkout
v3 (Yudai Takada). - Internal: follow standardrb code style (Yudai Takada).
- Internal: stop using Hound (Daniel Nolan).
- Internal: only run simplecov on C Ruby (Daniel Colson).
- Internal: quieter Cucumber (Daniel Colson).
- Internal: Ruby 3.2 support (Daniel Colson).
- Internal: Mike Burns is the CODEOWNER (Stefanni Brasil).
6.2.1 (March 8, 2022)
- Added: CI testing against truffleruby
- Changed: Documentation improvements for sequences and traits
- Fixed: ActiveSupport::Notifications reporting strategy through
associations now report as symbols
- BREAKING CHANGE: Custom strategies now need to define a
to_sym
method to specify the strategy identifier
- BREAKING CHANGE: Custom strategies now need to define a
- Fixed:
add_attribute
with reserved keywords assigns values correctly
6.2.0 (May 7, 2021)
- Added: support for Ruby 3.0
- Changed: Include factory or trait name in error messages for missing traits. d05a9a3c
- Changed: Switched from Travis CI to GitHub Actions
- Fixed: More Ruby 2.7 kwarg deprecation warnings
6.1.0 (July 8, 2020)
- Added: public reader for the evaluation instance, helpful for building interrelated associations
- Changed: raise a more helpful error when passing an invalid argument to an association
- Fixed: Ruby 2.7 kwarg deprecation warnings
6.0.2 (June 19, 2020)
- Fixed: bug causing traits to consume more memory each time they were used
6.0.1 (June 19, 2020)
- Fixed: bug with constant resolution causing unexpected uninitialized constant errors
6.0.0 (June 18, 2020)
- Added: automatic definition of traits for Active Record enum attributes, enabled by default
(Note that this required changing where factory_bot constantizes the build
class, which may affect applications that were using abstract factories for
inheritance. See issue #1409.) (This may break
FactoryBot.lint
because there may be previously non-existing factory+trait combinations being defined and checked) - Added:
traits_for_enum
method to define traits for non-Active Record enums - Added:
build_stubbed_starting_id=
option to define the starting id forbuild_stubbed
- Removed: deprecated methods on the top-level
FactoryBot
module meant only for internal use - Removed: support for EOL versions of Ruby (2.3, 2.4) and Rails (4.2)
- Removed: support for "abstract" factories with no associated class; use traits instead.
5.2.0 (April 24, 2020)
- Added: Pass index to block for
*_list
methods - Deprecated: methods on the top-level
FactoryBot
module meant only for internal use:callbacks
,configuration
,constructor
,initialize_with
,register_sequence
,resent_configuration
,skip_create
,to_create
5.1.2 (March 25, 2020)
- Fixed: Ruby 2.7 keyword deprecation warning in FactoryBot.lint
5.1.1 (October 2, 2019)
- Improved: performance of traits
- Fixed: registering strategies on JRuby
5.1.0 (September 21, 2019)
- Added: "Did you mean?" style error message to help with typos in association declarations
- Changed:
NoMethodError
for static attributes now offers a "Did you mean?" style message - Fixed: avoid undefining inherited evaluator methods
- Fixed: avoid stubbing id for records without a primary key
- Fixed: raise a helpful error for self-referencing traits to avoid a
SystemStackError
- Deprecated: methods on the top-level
FactoryBot
module meant only for internal use:allow_class_lookup
,allow_class_lookup
=,register_trait
,trait_by_name
,traits
,sequence_by_name
,sequences
,factory_by_name
,register_factory
,callback_names
,register_callback
,register_default_callbacks
,register_default_strategies
,strategies
5.0.2 (February 22, 2019)
- Bugfix: raise "Trait not registered" error when passing invalid trait arguments
5.0.1 (February 15, 2019)
- Bugfix: Do not raise error when two sequences have the same name in two traits that have the same name
5.0.0 (February 1, 2019)
- Added: Verbose option to include full backtraces in the linting output
- Changed: use_parent_strategy now defaults to true, so by default the build strategy will build, rather than create associations
- Changed: Passing a block when defining associations now raises an error
- Bugfix: use_parent_strategy is no longer reset by FactoryBot.reload
- Bugfix: rewind_sequences will now rewind local sequences along with the global ones
- Bugfix: the build_stubbed strategy now sets timestamps without changing the the original behavior of the timestamp methods
- Bugfix: avoid a stack error when referring to an "attributes" attribute in initialize_with
- Removed: support for EOL versions of Ruby and Rails
- Removed: static attributes (use dynamic attributes with a block instead)
- Removed: looking up factories by class
- Removed: ignore method (use transient instead)
- Removed: duplicate_attribute_assignment_from_initialize_with configuration option
- Deprecated: allow_class_lookup configuration option
4.11.1 (September 7, 2018)
- Documentation: Include .yardopts in the gem to fix broken RubyDoc links
4.11.0 (August, 15, 2018)
- Bugfix: Do not raise error for valid build_stubbed methods: decrement, increment, and toggle
- Bugfix: Do not add timestamps with build_stubbed for objects that shouldn't have timestamps
- 静的属性を廃止しました。
4.10.0 (May 25, 2018)
- Allow sequences to be rewound
4.9.0 (skipped - FactoryGirl only release)
4.8.2 (October 20, 2017)
- Rename factory_girl to factory_bot
4.8.1 (September 28, 2017)
- Explicitly define
#destroyed?
within theStub
strategy to returnnil
instead of raising - Update various dependencies
- Update internal test suite to use RSpec's mocking/stubbing instead of mocha
4.8.0 (December 16, 2016)
- ドキュメントを改善しました。
- Add
FactoryGirl.generate_list
to be consistent withbuild_list
/create_list
and friends - Add
FactoryGirl.use_parent_strategy
configuration to allow associations to leverage parent build strategy
4.7.0 (April 1, 2016)
- ドキュメントを改善しました。
- Improve instrumentation payload to include traits, overrides, and the factory itself
- トレイトをリントできるようにしました。
- Deprecate factory lookup by class name in preparation for 5.0
- Improve internal performance by using flat_map instead of map and compact
- Improve handling of dirty attributes after building a stubbed object
- Reduce warnings from redefining methods
4.6.0 (skipped)
4.5.0 (October 17, 2014)
- Improve FactoryGirl.lint by including exception and message in output
- Allow selective linting
- Use more explicit #public_send when doing attribute assignment
- Improve documentation around FactoryGirl.lint and initialize_with
- Deprecate #ignore in favor of #transient
4.4.0 (February 10, 2014)
- FactoryGirl.lintを加えました。
- Fix memory leak in duplicate traits
- ドキュメントを更新しました。
4.3.0 (November 3, 2013)
- Start testing against Rails 4.0 and Ruby 2.0.0
- Stop testing against Rails 3.0 and Ruby 1.9.2
- Add
*_pair
methods to only build two objects - Raise if a method is defined with a FactoryGirl block (factory or trait)
- Allow use of Symbol#to_proc in callbacks
- 大域コールバックを加えました。
- Improve GETTING_STARTED and README
4.2.0 (January 18, 2013)
- ドキュメントを改善しました。
- Allow
*_list
syntax methods to accept a block - Update gem dependencies
- Allow setting id for objects created with
build_stubbed
- Fix Stub strategy to mimic ActiveRecord regarding
created_at
- Evaluate sequences within the context of an Evaluator
- Fix Mocha deprecation warning
- Fix some warnings when running RUBYOPT=-w rake
- Convert test suite to RSpec's "expect" syntax
4.1.0 (September 11, 2012)
- Allow multiple callbacks to bind to the same block
- Fix documentation surrounding the stub strategy
4.0.0 (August 3, 2012)
- Remove deprecated cucumber_steps
- Remove deprecated alternate syntaxes
- Deprecate duplicate_attribute_assignment_from_initialize_with, which is now unused as attributes assigned within initialize_with are not subsequently assigned
3.6.1 (August 2, 2012)
Update README to include info about running with JRuby
- Update dependencies on RSpec and tiny versions of Rails in Appraisal
- Improve flexibility of using traits with associations and add documentation
- Stub update_column to raise to mirror ActiveRecord's change from update_attribute
3.6.0 (July 27, 2012)
- Code/spec cleanup
- Allow factories with traits to be used in associations
- Refactor Factory to use DefinitionHierarchy to handle managing callbacks, custom constructor, and custom to_create
- Add memoization to speed up factories providing attribute overrides
- Add initial support of JRuby when running in 1.9 mode
- Improve docs on what happens when including FactoryGirl::Syntax::Methods
3.5.0 (June 22, 2012)
- Allow created_at to be set when using build_stubbed
- Deprecate FactoryGirl step definitions
3.4.2 (June 19, 2012)
- Fix bug in traits with callbacks called implicitly in factories whose callbacks trigger multiple times
3.4.1 (June 18, 2012)
- Fix traits so they can be nested and referred to from other traits
3.4.0 (June 11, 2012)
- Sequences support Enumerators
- Optionally disable duplicate assignment of attributes in initialize_with
- Make hash of public attributes available in initialize_with
- Support referring to a factory based on class name
3.3.0 (May 13, 2012)
- to_create、skip_create、initialize_withを大域的に定義できるようにしました。
- to_create、skip_create、initialize_withをトレイト内で定義できるようにしました。
- Fix deprecation messages for alternate syntaxes (make, generate, etc.)
- Improve library documentation
- Deprecate after_build, after_create, before_create, after_stub in favor of new callbacks
- Introduce new callback syntax: after(:build) {}, after(:custom) {}, or callback(:different) {} This allows for declaring any callback, usable with custom strategies
- Add attributes_for_list and build_stubbed_list with the StrategySyntaxMethodRegistrar
- Allow use of syntax methods (build, create, generate, etc) implicitly in callbacks
- Internal refactoring of a handful of components
3.2.0 (April 24, 2012)
- Use AS::Notifications for pub/sub to track running factories
- Call new within initialize_with implicitly on the build class
- Skip to_create with skip_create
- Allow registration of custom strategies
- Deprecate alternate syntaxes
- Implicitly call factory_bot's syntax methods from dynamic attributes
3.1.0 (April 6, 2012)
- Sequences support aliases, which reference the same block
- ドキュメントを更新しました。
- before_createコールバックを加えました。
- Support use of #attribute_names method to determine available attributes for steps
- Use ActiveSupport::Deprecation for all deprecations
3.0.0 (March 23, 2012)
- Deprecate the vintage syntax
- Remove Rails 2.x support
- Remove Ruby 1.8 support
- Remove deprecated features, including default_strategy, factory_name, :method for defining default strategy, ignore on individual attributes, and interacting with Factory the way you would FactoryGirl
2.6.4 (March 16, 2012)
- 一過的属性名を無視しないようにしました。
- Ensure attributes set on instance are calculated uniquely
2.6.3 (March 9, 2012)
- Fix issue with traits not being present the first time a factory is accessed
- Update available Cucumber step definitions to not require a trailing colon when building a table of attributes to instantiate records with
2.6.2 (March 9, 2012)
- Allow factories to use all their ancestors' traits
- Ignore bin dir generated by bundler
- Namespace ::Factory as top-level to fix vintage syntax issue with Ruby 1.9.2-p3p18
2.6.1 (March 2, 2012)
- Use FactoryGirl.reload in specs
- Clean up running named factories with a particular strategy with FactoryGirl::FactoryRunner
2.6.0 (February 17, 2012)
- Improve documentation of has_many associations in the GETTING_STARTED document
- Deprecate :method in favor of :strategy when overriding an association's build strategy
2.5.2 (February 10, 2012)
- Fix step definitions to use associations defined in parent factories
- Add inline trait support to (build|create)_list
- Update ActiveSupport dependency to >= 2.3.9, which introduced class_attribute
2.5.1 (February 3, 2012)
- Fix attribute evaluation when the attribute isn't defined in the factory but is a private method on Object
- Update rubygems on Travis before running tests
- Fix spec name
- Update GETTING_STARTED with correct usage of build_stubbed
- READMEを更新し、initialize_withについて追記しました。
- Honor :parent on factory over block nesting
2.5.0 (January 20, 2012)
- Revert 'Deprecate build_stubbed and attributes_for'
- Implement initialize_with to allow overriding object instantiation
- Ensure FG runs against Rails 3.2.0
2.4.2 (January 18, 2012)
- 行内トレイトと既定のファクトリとの相互作用を修正しました。
2.4.1 (January 17, 2012)
- Deprecate build_stubbed and attributes_for
- 行内トレイトを修正しました。
2.4.0 (January 13, 2012)
- Refactor internals of FactoryGirl to use anonymous class on which attributes get defined
- Explicitly require Ruby 1.8.7 or higher in gemspec
- ドキュメントを修正しました。
- Add Gemnasium status to documentation
- Supplying a Class to a factory that overrides to_s no longer results in getting the wrong Class constructed
- Be more agnostic about ORMs when using columns in FactoryGirl step definitions
- Test against Active Record 3.2.0.rc2
- Update GETTING_STARTED to use Ruby syntax highlighting
2.3.2 (November 26, 2011)
- Move logic of where instance.save! is set to Definition
- Fix method name from aliases_for? to alias_for?
- Refactor internal attribute handling to use an anonymous class instead of faking Ruby's variable resolution. This allows for more sane usage of attributes without having to manage sorting priority because attributes can turn themselves into procs, which are used with define_method on a class so attributes work correctly all the time.
2.3.1 (November 23, 2011)
- Remove internally-used associate method from all the FactoryGirl::Proxy subclasses
- Move around requiring of files
- Consolidate errors into factory_bot.rb
- Refactor AttributeList to deal with priority only when iterating over attributes
- Refactor internals of some of the Proxy subclasses
- Ensure callbacks on traits are executed in the correct order
2.3.0 (November 18, 2011)
- Registries are named, resulting in better messages when factories, traits, or sequences cannot be found
- Fix incorrect tests
- Internals refactoring introducing FactoryGirl::NullFactory, FactoryGirl::Definition, and FactoryGirl::DeclarationList
- Use ActiveSupport for Hash#except and its delegation capabilities
- Fix usage of callbacks when added via implicit traits
- Use Bundler tasks and clean up dependencies
- Fix failing spec for big letters in factory name passed as symbol
- Add ability for traits to be added dynamically when creating an instance via build, create, build_stubbed, or attributes_for
2.2.0 (October 14, 2011)
- Clean up RSpec suite to not use 'should'
- Use create_list in step definitions
- Syntax methods that deal with ORM interaction (attributes_for, build, build_stubbed, and create) now accept a block that yields the result. This results in a more convenient way to interact with the result than using Object.tap.
- Standardize deprecation warnings
- Update transient attribute syntax to use blocks instead of calling ignore on each attribute declaration
- Parents can be defined after children because factories are evaluated when they're used; this means breaking up factories across multiple files will behave as expected
- Large internal refactoring, including changing access modifiers for a handful of methods for a more clearly defined API
2.1.2 (September 23, 2011)
- Bugfix: Vintage syntax fixed after bug introduced in 2.1.1
- Introduce dependency on activesupport to remove code from Factory class
2.1.1 (September 23, 2011) (yanked)
- Bugfix: Parent object callbacks are run before child object callbacks
- Declarations: allow overriding/modification of individual traits in child factories
- Callbacks refactored to not be attributes
- Updating documentation for formatting and clarity (incl. new specificity for cucumber)
2.1.0 (September 02, 2011)
- Bugfix: created_at now defined for stubbed models
- Gemspec updated for use with Rails 3.1
- Factories can now be modified post-definition (useful for overriding defaults from gems/plugins)
- All factories can now be reloaded with Factory.reload
- Add :method => build to factory associations to prevent saving of associated objects
- Factories defined in {Rails.root}/factories are now loaded by default
- Various documentation updates
1.1.4 (November 28, 2008)
- Factory.build now uses Factory.create for associations of the built object
- Factory definitions are now detected in subdirectories, such as factories/person_factory.rb (thanks to Josh Nichols)
- Factory definitions are now loaded after the environment in a Rails project (fixes some issues with dependencies being loaded too early) (thanks to Josh Nichols)
- Factory names ending in 's' no longer cause problems (thanks to Alex Sharp and Josh Owens)
1.1.3 (September 12, 2008)
- 自動的にfactories.rb、test/factories.rb、spec/factories.rbから定義を取り入れるようにしました。
1.1.2 (July 30, 2008)
- Improved error handling for invalid and undefined factories/attributes
- Improved handling of strings vs symbols vs classes
- Added a prettier syntax for handling associations
- Updated documentation and fixed compatibility with Rails 2.1
1.1.1 (June 23, 2008)
- The attribute "name" no longer requires using #add_attribute
1.1.0 (June 03, 2008)
- 依存属性の対応を加えました。
- Fixed the attributes_for build strategy to not build associations
- Added support for sequences
1.0.0 (May 31, 2008)
- First version
プロジェクト名の歴史
Factory Girl
このライブラリは2008年に「Factory Girl」という名前で初回リリースされました。
デザインパターン本のソフトウェアパターンであるファクトリーメソッドと母オブジェクトの理念に共感して名前を選びました。 また同名のローリングストーンズの歌への参照にもなっています。
Factory Bot
「Factory Girl」という名前は、このライブラリを知った開発者や攻撃的ないし問題があるように感じたりする人にとって困惑するものでした。 2017年10月に「Factory Bot」にライブラリを改名しました。
Factory Botへの貢献
どなたでもプルリクエストを歓迎します。 このプロジェクトに参加することで、thoughtbotの行動規範に従うことに同意したものとします。
以下はあなたが貢献できる方法です。
- アルファ、ベータ、プレリリースのバージョンを使う
- 不具合を報告する
- 新機能を提案する
- ドキュメントを書いたり編集したりする
- 仕様を書く
- コードを書く(どんなパッチも些細ではありません。誤植修正やコメントの追加などもそうです)
- コードを改修する
- イシューを閉じる
- パッチをレビューする
イシューを送る
- GitHubのイシュートラッカーを使って不具合と機能と把握しています。
- 不具合報告や機能の要望を送る前に、既に送られていないか必ずお確かめください。
- 不具合報告を送るとき、再現スクリプトとその不具合を再現するのに必要かもしれないその他の詳細を含めてください。 これにはgemのバージョン、Rubyのバージョン、オペレーティングシステムが含まれます。
イシューを整頓する
- 送信者から反応がないイシューは30日以降に閉じられます。
- イシューは修正されたり回答されたと思われたときに閉じられます。 メンテナが間違っていたときは再び開くことがあります。
- イシューが間違って閉じられたときはイシューを理解して説明してください。 喜んでイシューを再度開きます。
プルリクエストを送る
- 公式リポジトリをフォークします。
- トピックブランチを作ります。
- 機能や不具合修正を実装します。
- 変更を加え、コミットし、プッシュします。
- プルリクエストを送る。
補足
- コードを変えたときはテストを加えてください。 テストのない貢献は受け付けられません。
- テストの加え方が分からなければ、PRを作って手助けを求めるコメントを残してください。 喜んでお手伝いします!
- gemのバージョンを更新しないでください。
準備
bundle install
テストスートを走らせる
既定のrakeタスクでは、完全なテストスートとstandardが実行されます。
bundle exec rake
1グループのテスト(単体、スペック、機能)を走らせることもできます。
bundle exec rake spec:unit
bundle exec rake spec:acceptance
bundle exec rake features
個別のrspecのテストを走らせるため、パスと行番号を与えられます。
bundle exec rspec spec/path/to/spec.rb:123
appraisalで特定のバージョンのrailsでテストを走らせられます。 Rails 6に対して、既定のrakeタスクを走らせる例は以下です。
bundle exec appraisal 6.0 rake
整形
自動でコードを整形するにはstandardを使ってください。
bundle exec rake standard:fix
https://github.com/middleman/middleman-heroku/blob/master/CONTRIBUTING.md から着想を得ました。
この日本語訳について
本文書は``The factory_bot book''の日本語訳です。
日本語訳のファイル(Markdown形式)は原文のファイル構造に従って生成され、translation/ja
ディレクトリ以下に配置されます。
特にtranslation/ja/docs
ディレクトリ以下に配置されるファイルはmdBookによりウェブページに変換されます。
翻訳管理にはpo4aが使われており、GNU GettextのPO形式により翻訳が保管されます。
そのため、原文に変更が発生した場合は次の手順で更新します。
なお、お知らせは過去のバージョンは未翻訳のものが残っています。
これらのバージョンは必要に応じて訳出することとします。
POファイルではtranslation/po/news.ja.po
に分離されています。
- 原文のリポジトリに合わせてリベースする。
make -C translation
でPOファイルを更新する。- 更新された
translation/po/*.ja.po
を編集し、fuzzyの項目や未翻訳の項目の翻訳を完了する。 - 再度
make -C translation
を実行し、日本語訳のファイルを生成する。