この記事は、factory_bot の Rails
の場合だけを翻訳&わかりやすく補足を追加したものになります。
Gemfileを更新する(Update Your Gemfile)
Railsを使用している場合:
gem "factory_bot_rails"
Gemfileが更新されたら、バンドルを更新する必要があります。
bundle install
テストスイートを構成する(Configure your test suite)
RSpec
Railsを使用している場合、次の設定を spec/support/factory_bot.rb
に追加し、rails_helper.rb
にそのファイルが必要であることを確認してください:
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
※テストスイートに FactoryBot::Syntax::Methods
を含めない場合、すべてのfactory_botメソッドの前に FactoryBot
を付ける必要があります。
FactoryBot.create(:user)
# OK
create(:user)
ファクトリーの定義(Defining factories)
各ファクトリーには、名前と一連の属性があります。
名前は、デフォルトでオブジェクトのクラスを推測するために使用されます。
# これはUserクラスを推測します
FactoryBot.define do
factory :user do
first_name { "John" }
last_name { "Doe" }
admin { false }
end
end
クラスを明示的に指定することもできます。
# これはUserクラスを使用します(そうでなければ、Adminが推測されます)
factory :admin, class: User do
定数が利用できない場合
(たとえば、モデルのロードを待機するRailsエンジンを使用している場合)
シンボルまたは文字列を渡すこともできます。
オブジェクトの構築を開始すると、factory_bot
が後で定数化されます。
# Doorkeeper::AccessTokenがまだロードされていなくても問題ありません
factory :access_token, class: "Doorkeeper::AccessToken"
Rubyのブロック構文のため、属性を「ハッシュ」として定義します。
(例、serialized / JSON列には、2組の中括弧が必要です。
factory :program do
configuration { { auto_resolve: false, auto_define: true } }
end
そのクラスのインスタンスを作成するために必要な属性の最も単純なセットを提供する各クラスに1つのファクトリーを持つことを強くお勧めします。
ActiveRecordオブジェクトを作成している場合、検証によって必要とされ、デフォルトを持たない属性のみを提供する必要があることを意味します。
継承を通じて他のファクトリーを作成して、各クラスの一般的なシナリオをカバーできます。
注:同じ名前の複数のファクトリーを定義しようとすると、エラーが発生します。
ファクトリーはどこでも定義できますが、後で自動的にロードされます。
下記の場所にファクトリーが定義されている場合、 FactoryBot.find_definitions
を呼び出します。
spec/factories/*.rb
ファクトリーの使用方法(Using factories)
factory_bot
は、いくつかの異なるビルドをサポートしています。
# 保存されていないUserインスタンスを返します
user = build(:user)
# 保存されたUserインスタンスを返します
user = create(:user)
# Userインスタンスの構築に使用できる属性のハッシュを返します
attrs = attributes_for(:user)
# すべての定義済み属性がスタブアウトされたオブジェクトを返します
stub = build_stubbed(:user)
# 上記のメソッドのいずれかにブロックを渡すと、戻りオブジェクトが生成されます
create(:user) do |user|
user.posts.create(attributes_for(:post))
end
使用するファクトリーに関係なく、ハッシュを渡すことで定義済みの属性をオーバーライドできます。
# ユーザーインスタンスをビルドし、first_nameプロパティをオーバーライドします
user = build(:user, first_name: "Joe")
user.first_name
# => "Joe"
エイリアス(Aliases)
factory_bot
を使用すると、既存のファクトリーのエイリアスを定義して、再利用しやすくすることができます。
例えば、Postオブジェクトに実際にUserクラスのインスタンスを参照するauthor属性がある場合に便利です。
通常、factory_bot
は関連付け名からファクトリー名を推測できますが、この場合、作成者ファクトリーを探しますが無駄です。
そのため、エイリアスネームの下で使用できるように、ユーザーファクトリーにエイリアスを作成します。
factory :user, aliases: [:author, :commenter] do
first_name { "John" }
last_name { "Doe" }
date_of_birth { 18.years.ago }
end
factory :post do
author
# instead of
# association :author, factory: :user
title { "How to read a book effectively" }
body { "There are five steps involved." }
end
factory :comment do
commenter
# instead of
# association :commenter, factory: :user
body { "Great article!" }
end
依存属性(Dependent Attributes)
属性は、エバリュエーターを使用して他の属性の値に基づくことができます
動的属性ブロックに渡されます:
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 Attributes)
一時的な属性をファクトリーに渡すことで、DRY原則でコードを書くことができます。
factory :user do
transient do
rockstar { true }
upcased { false }
end
name { "John Doe#{" - Rockstar" if rockstar}" }
email { "#{name.downcase}@example.com" }
after(:create) do |user, evaluator|
user.name.upcase! if evaluator.upcased
end
end
create(:user, upcased: true).name
#=> "JOHN DOE - ROCKSTAR"
一時属性は、モデルに設定し属性が存在する場合でも、上書きしようとする場合でも。
attributes_for
内では無視されません。
メソッド名/予約語の属性(Method Name/Reserved Word Attributes)
属性が既存のメソッドまたは予約語(DefinitionProxyクラスのすべてのメソッド)と競合する場合、それらを定義できます。add_attribute
factory :dna do
add_attribute(:sequence) { 'GATTACA' }
end
factory :payment do
add_attribute(:method) { 'paypal' }
end
継承(Inheritance)
ファクトリーをネストすることにより、共通の属性を繰り返すことなく、同じクラスに対して複数のファクトリーを簡単に作成できます。
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
上記のように、各クラスの基本的なファクトリーを定義することをお勧めします。
作成に必要な属性のみが含まれます。次に、より具体的に作成します。
この基本的な親から継承するファクトリーの定義は、DRY原則になります。
関連付け(Associations)
ファクトリー内で関連付けを設定することは可能です。
ファクトリー名が関連付け名と同じ場合、ファクトリー名は省略できます。
factory :post do
# ...
author
end
別のファクトリーを指定するか、属性をオーバーライドすることもできます。
factory :post do
# ...
association :author, factory: :user, last_name: "Writely"
end
factory_bot 5
では、親オブジェクトへの関連付けはデフォルトで同じビルド戦略を使用します。
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
# ユーザーと投稿を作成して保存します。
post = create(:post)
post.new_record? # => false
post.author.new_record? # => false
# ユーザーをビルドして保存し、ビルドしますが投稿は保存しません。
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
# ユーザーを作成してから、投稿を作成しますが、どちらも保存しません
post = build(:post)
post.new_record? # => true
post.author.new_record? # => true
strategy::build
オプションはassociation
の明示的な呼び出しに渡す必要があることに注意してください。
暗黙の関連付けでは使用できません。
factory :post do
# ...
author strategy: :build # <<<これは動作しません。author_idがnilになります。
「has_many」関係のデータの生成はもう少し複雑です。
必要な柔軟性の量に応じて、しかし関連データの生成で確実な例があります。
FactoryBot.define do
# ユーザーの `belongs_to`アソシエーションでファクトリーをポストする
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, evaluator|
create_list(:post, evaluator.posts_count, user: user)
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
「has_and_belongs_to_many」関係のデータの生成は非常に似ています。
上記の「has_many」関係に、わずかな変更を加えて、単一ではなく、
モデルの複数形の属性名に対するオブジェクトの配列を生成します。
以下は、単一バージョンの属性名のオブジェクトを介して関連する2つのモデル作成の例です。
has_and_belongs_to_many
:
FactoryBot.define do
# プロファイルの「belongs_to」関連付けを持つ言語ファクトリー
factory :language do
title { "Through the Looking Glass" }
profile
end
# 言語が関連付けられていないプロファイルファクトリー
factory :profile do
name { "John Doe" }
# profile_with_languagesは、profile が作成された後にlanguagesを作成します。
factory :profile_with_languages do
# languages_countは無視される属性として宣言されており、
# ファクトリーの属性、およびエバリュエーターを介したコールバック
transient do
languages_count { 5 }
end
# after(:create)は2つの値を生成します。プロファイルインスタンス自体
# エバリュエーター。ファクトリーを含むすべての値を格納します。
# 属性を無視しました。 `create_list`の2番目の引数は、
# 作成するレコード。プロファイルが適切に関連付けられていることを確認します。
after(:create) do |profile, evaluator|
create_list(:language, evaluator.languages_count, profiles: [profile])
end
end
end
end
これにより、下記のことが可能になります。
create(:profile).languages.length # 0
create(:profile_with_languages).languages.length # 5
create(:profile_with_languages, languages_count: 15).languages.length # 15
ポリモーフィックな関連付け
ポリモーフィックな関連付けは、特性を使用して処理できます。
FactoryBot.define do
factory :video
factory :photo
factory :comment do
for_photo # default to the :for_photo trait if none is specified
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)
シーケンス(Sequences)
特定の形式(たとえば、電子メールアドレス)の一意の値はシーケンスを使用して生成されます。
#新しいシーケンスを定義
FactoryBot.define do
sequence :email do |n|
"person#{n}@example.com"
end
end
generate :email
# => "person1@example.com"
generate :email
# => "person2@example.com"
シーケンスは、動的属性で使用できます。
factory :invite do
invitee { generate(:email) }
end
または暗黙的な属性としてシーケンスを定義すると、シーケンスと同じ名前のファクトリーができます。
factory :user do
email # `email {generate(:email)}`と同じ
end
また、特定のファクトリーでのみ使用されるインラインシーケンスを定義することもできます。
factory :user do
sequence(:email) { |n| "person#{n}@example.com" }
end
初期値をオーバーライドすることもできます。
factory :user do
sequence(:email, 1000) { |n| "person#{n}@example.com" }
end
ブロックがない場合、値はその初期値から始まり、自動的に増加します。
factory :post do
sequence(:position)
end
シーケンスにはエイリアスも設定できます。シーケンスエイリアスは同じカウンターを共有します。
factory :user do
sequence(:email, 1000, aliases: [:sender, :receiver]) { |n| "person#{n}@example.com" }
end
# senderおよび:receiverによって共有される:emailの値カウンターを増加します
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」などになります。
シーケンスは FactoryBot.rewind_sequences
で巻き戻すこともできます。
これにより、登録されているすべてのシーケンスが巻き戻されます。
sequence(:email) {|n| "person#{n}@example.com" }
generate(:email) # "person1@example.com"
generate(:email) # "person2@example.com"
generate(:email) # "person3@example.com"
FactoryBot.rewind_sequences
generate(:email) # "person1@example.com"
特性(Traits)
trait
を使用すると、どのファクトリーにも属性をグループ化してから適用できます
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
注:暗黙的な属性として特性を定義することは機能しないことに注意してください。
同じ属性を定義する特性は、AttributeDefinitionErrors
を発生させません。
最新の属性を定義する特性が優先されます。
factory :user do
name { "Friendly User" }
login { name }
trait :male do
name { "John Doe" }
gender { "Male" }
login { "#{name} (M)" }
end
trait :female do
name { "Jane Doe" }
gender { "Female" }
login { "#{name} (F)" }
end
trait :admin do
admin { true }
login { "admin-#{name}" }
end
factory :male_admin, traits: [:male, :admin] # login will be "admin-John Doe"
factory :female_admin, traits: [:admin, :female] # login will be "Jane Doe (F)"
end
サブクラスのtrait
によって付与された個々の属性をオーバーライドすることもできます。
factory :user do
name { "Friendly User" }
login { name }
trait :male do
name { "John Doe" }
gender { "Male" }
login { "#{name} (M)" }
end
factory :brandon do
male
name { "Brandon" }
end
end
factory_bot
からインスタンスを構築するときに、特性をシンボルのリストとして渡すこともできます。
factory :user do
name { "Friendly User" }
trait :male do
name { "John Doe" }
gender { "Male" }
end
trait :admin do
admin { true }
end
end
# 性別が「男性」で名前が「Jon Snow」の管理ユーザーを作成します
create(:user, :admin, :male, name: "Jon Snow")
この機能は、「build」、「build_stubbed」、「attributes_for」、および「create」で機能します。
create_list
および build_list
メソッドもサポートされています。
# 複数のレコード作成
factory :user do
name { "Friendly User" }
trait :admin do
admin { true }
end
end
# 性別が「男性」で名前が「Jon Snow」の3人の管理ユーザーを作成します
# 2つ目のパラメーターとして作成/ビルドするインスタンスの数(3)
# 複数のレコード作成
create_list(:user, 3, :admin, :male, name: "Jon Snow")
特性は、関連付け(associations)でも簡単に使用できます。
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'
# or
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, evaluator|
create :line_item, invoice: invoice, amount: evaluator.amount
end
end
end
create :invoice, :with_amount, amount: 2
コールバック(Callbacks)
factory_bot
は、いくつかのコードを挿入するための4つのコールバックを使用可能です。
- after(:build)-ファクトリーのビルド後に呼び出されます(
FactoryBot.build
、FactoryBot.create
を介して) - before(:create)-ファクトリーが保存される前に呼び出されます(
FactoryBot.create
を介して) - after(:create)-ファクトリーが保存された後に呼び出されます(
FactoryBot.create
を介して) - after(:stub)-ファクトリーがスタブ化された後に呼び出されます(
FactoryBot.build_stubbed
を介して)
例:
# ビルド後に generate_hashed_password メソッドを呼び出すファクトリーを定義します。
factory :user do
after(:build) { |user| generate_hashed_password(user) }
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
に依存するコールバックを呼び出すこともできます:
# 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) # ユーザーを作成して確認する
ファクトリーの変更(Modifying factories)
一連のファクトリー(たとえば、gem開発者から)を与えられたが、それらをアプリケーションにより適合させるために変更したい場合は、次のことができます。
子ファクトリーを作成して属性を追加する代わりに、そのファクトリーを変更します。
gem
がユーザーファクトリーを提供する場合:
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 }
gender { "Female" }
health { 90 }
end
end
代わりにそのファクトリーを変更できます。
FactoryBot.modify do
factory :user do
full_name { "Jane Doe" }
date_of_birth { 21.years.ago }
gender { "Female" }
health { 90 }
end
end
ファクトリーを変更するときは、コールバックを除き、必要な属性を変更できます。
ファクトリーに対して異なる動作をするため、FactoryBot.modify
は FactoryBot.define
ブロックの外側で呼び出す必要があります。
警告:変更できるのは、ファクトリー(シーケンスまたはトレイトではない)とコールバックのみです(通常の場合と同じように*複合されます)。
だから、もし変更しているファクトリーは after(:create)
コールバックを定義し、after(:create)
を定義してもそれはオーバーライドされません。最初のコールバックの後に実行されます。
複数のレコードの作成または作成(Building or Creating Multiple Records)
場合によっては、ファクトリーの複数のインスタンスを一度に作成または構築する必要があります。
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)
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) # 属性ハッシュの配列
リンティングファクトリー(Linting Factories)
factory_bot
は、既知のファクトリーのリントを許可します:
FactoryBot.lint
FactoryBot.lint
は各ファクトリーを作成し、発生した例外をキャッチします
作成プロセス中。 FactoryBot :: InvalidFactoryError
は
可能性のある工場の工場(および対応する例外)のリスト作成されません。
FactoryBot.lint
の推奨される使用法は、タスクで実行することです。
テストスイートが実行される前、before(:suite)
で実行します。
※:あなたのテストの単一のテストを実行するときパフォーマンスに悪影響を及ぼします。
Rakeタスクの例:
# lib/tasks/factory_bot.rake
namespace :factory_bot do
desc "すべてのFactoryBotファクトリーが有効であることを確認"
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
上記の例は、SQLトランザクションとロールバックを使用して、データベースをクリーンなままにします。
リントしたいファクトリーのみを渡すことで、ファクトリーを選択的にリントできます。
factories_to_lint = FactoryBot.factories.reject do |factory|
factory.name =~ /^old_/
end
FactoryBot.lint factories_to_lint
これにより、「old_」という接頭辞が付いていないすべてのファクトリーがリントされます。
特性もリントできます。このオプションは、それぞれがファクトリーのすべての特性は、有効なオブジェクトを独自に生成します。
これは、traits: true
を lint
メソッドに渡すことで有効になります:
FactoryBot.lint traits: true
これは、他の引数と組み合わせることもできます。
FactoryBot.lint factories_to_lint, traits: true
リンティングに使用される戦略を指定することもできます。
FactoryBot.lint strategy: :build
詳細なリンティングには、デバッグに役立つ各エラーの完全なバックトレースが含まれます。
FactoryBot.lint verbose: true
カスタム構造(Custom Construction)
factory_bot
を使用して、いくつかの属性を持つオブジェクトを構築する場合
initialize
に渡されるか、単純な以外の何かをしたい場合、ビルドクラスで new
を呼び出すと、デフォルトの動作をオーバーライドできます。
ファクトリーで「initialize_with」を定義します。
例:
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との最大限の互換性のために、デフォルトのinitialize
は、ビルドクラスで 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)
# runs
User.new('value')
オブジェクトを永続化するカスタムメソッド(Custom Methods to Persist Objects)
デフォルトでは、レコードを作成すると、インスタンスで「保存」が呼び出されます。それは常に理想的であるとは限らない可能性があります。
ファクトリーの 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 Instrumentation
作成された工場(および構築戦略)を追跡するために、
「ActiveSupport::Notifications
」は、サブスクライブする方法を提供するために含まれています
1つの例は、に基づいてファクトリーを追跡することです
実行時間のしきい値。
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
RailsプリローダーとRSpec
spring
や zeus
などのRailsプリローダーでRSpecを実行すると、
ファクトリーを作成するときに 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つの可能な解決策は、プリローダーなしでスイートを実行するか、
または次のように、 FactoryBot.reload
をRSpec設定に追加します。
RSpec.configure do |config|
config.before(:suite) { FactoryBot.reload }
end