- Update Your Gemfile
- テストスイートの設定
- Test::Unit
- Cucumber
- Spinach
- Minitest
- Minitest:.NET
- Minitest::Spec
- minitest-rails
- Defining factories
- Using factories
- Dependent Attributes
- Transient Attributes
- 継承
- Associations
- Traits
- コールバック
- Building or Creating Multiple Records
- Linting Factories
- Custom Construction
- カスタム戦略
- オブジェクトを永続化するカスタム メソッド
- ActiveSupport Instrumentation
- Rails プリローダーと RSpec
- Bundler なしで使用する場合
Update Your Gemfile
Railsを使っている場合は、factory_girl_rails
:
gem "factory_girl_rails", "~> 4.0"
Railsを使っていない場合は、factory_girl
:
gem "factory_girl", "~> 4.0"
export JRUBY_OPTS=--1.9
Gemfileが更新できたら、バンドルの更新を行いましょう。
テストスイートの設定
サポートフォルダがイーガーリーロードされないので、rails_helper で上記のファイルを要求するのを忘れないでください
require 'support/factory_girl'
Test::Unit
class Test::Unit::TestCase include FactoryGirl::Syntax::Methodsend
Cucumber
# env.rb (Rails example location - RAILS_ROOT/features/support/env.rb)World(FactoryGirl::Syntax::Methods)
Spinach
class Spinach::FeatureSteps include FactoryGirl::Syntax::Methodsend
Minitest
class Minitest::Unit::TestCase include FactoryGirl::Syntax::Methodsend
Minitest:.NET
class Minitest::Unit::TestCase include FactoryGirl::Syntax::Methodsend
Minitest::Spec
class Minitest::Spec include FactoryGirl::Syntax::Methodsend
minitest-rails
class ActiveSupport::TestCase include FactoryGirl::Syntax::Methodsend
テスト スイートに FactoryGirl::Syntax::Methods
を含めない場合、すべての factory_girl メソッドの前に FactoryGirl
を付ける必要があります。
Defining factories
各ファクトリーは名前と属性のセットを持っています。 名前は、デフォルトではオブジェクトのクラスを推測するために使用されますが、明示的に指定することも可能です。
そのクラスのインスタンスを作成するために必要な属性の最も単純なセットを提供する、各クラスに 1 つのファクトリを持つことが強く推奨されます。 ActiveRecord オブジェクトを作成する場合、これは、検証によって必要とされ、デフォルトを持たない属性のみを提供する必要があることを意味します。
同じ名前で複数のファクトリーを定義しようとすると、エラーが発生します。
ファクトリーはどこでも定義できますが、ファクトリーが次の場所にあるファイルで定義されている場合、FactoryGirl.find_definitions
を呼び出した後に自動的に読み込まれます:
test/factories.rbspec/factories.rbtest/factories/*.rbspec/factories/*.rb
Using factories
factory_girl は複数の異なる構築戦略をサポートしています。
どの戦略を使用する場合でも、ハッシュを渡すことにより、定義された属性を上書きすることが可能です。 これらの「動的」属性は、パラメータの代わりにブロックを渡すことで追加できます。
factory :user do # ... activation_code { User.generate_activation_code } date_of_birth { 21.years.ago }end
Ruby のブロック構文のため、属性を Hash
es として定義すると (たとえば、シリアライズ/JSON 列の場合)、2 組の中括弧が必要です。 これは、たとえば、Post オブジェクトが、実際には User クラスのインスタンスを参照する author 属性を持っている場合に便利でしょう。 通常 factory_girl はアソシエーション名からファクトリー名を推測しますが、この場合、無駄に author ファクトリーを探してしまいます。
Dependent Attributes
Attributes can be based on the values of other attributes using the evaluates that is yield to dynamic attribute blocks.
Transient Attributes
Transient attribute to factory でコードを DRY アップできる場合があります。
Static および Dynamic 属性は、Transient 属性として作成することができます。 Transient 属性は attributes_for 内では無視され、属性が存在するか、それをオーバーライドしようとした場合でも、モデルには設定されません。 factory_girl コールバックで評価者にアクセスする必要がある場合は、2 番目のブロック引数 (評価者用) を宣言し、そこからトランジェントな属性にアクセスする必要があります。
factory :dna do add_attribute(:sequence) { 'GATTACA' }endfactory :payment do add_attribute(:method) { 'paypal' }end
継承
ファクトリーを入れ子にすることにより、共通の属性を繰り返すことなく、同じクラスに対して複数のファクトリーを簡単に作成できます:
また、親を明示的に割り当てることもできます:
factory :post do title "A title"endfactory :approved_post, parent: :post do approved trueend
上述のように、各クラスに対して基本ファクトリーとそれを作るために必要な属性のみを定義することは良い習慣と言えます。 次に、この基本的な親を継承する、より具体的なファクトリーを作成します。 ファクトリーの定義はコードなので、DRY に保つこと。
Associations
ファクトリー内に関連付けを設定することが可能です。 ファクトリー名がアソシエーション名と同じ場合は、ファクトリー名を省くことができます。
factory :post do # ... authorend
別のファクトリーやオーバーライド属性を指定することもできます。
factory :post do # ... association :author, factory: :user, last_name: "Writely"end
親オブジェクトに使用される構築戦略によって、アソシエーションメソッドの動作は変化します。
関連付けられたオブジェクトを保存しない場合は、ファクトリーに strategy: :build を指定します。
strategy: :build
オプションは、association
への明示的な呼び出しに渡される必要があり、暗黙の関連付けでは使用できないことに注意してください:
factory :post do # ... author strategy: :build # <<< this does *not* work; causes author_id to be nil
has_many
関係のデータの生成は、必要な柔軟性に応じて少し複雑になりますが、ここでは確実に関連データを生成する例です。
FactoryGirl.define do # post factory with a `belongs_to` association for the user factory :post do title "Through the Looking Glass" user end # user factory without associated posts factory :user do name "John Doe" # user_with_posts will create post data after the user has been created factory :user_with_posts do # posts_count is declared as a transient attribute and available in # attributes on the factory, as well as the callback via the evaluator transient do posts_count 5 end # the after(:create) yields two values; the user instance itself and the # evaluator, which stores all values from the factory, including transient # attributes; `create_list`'s second argument is the number of records # to create and we make sure the user is associated properly to the post after(:create) do |user, evaluator| create_list(:post, evaluator.posts_count, user: user) end end endend
これにより、次のことが可能になります。
create(:user).posts.length # 0create(:user_with_posts).posts.length # 5create(:user_with_posts, posts_count: 15).posts.length # 15
has_and_belongs_to_many
リレーションのデータを生成することは、上記の has_many
リレーションと非常に似ていますが、小さな変更があり、単一バージョンの属性名には単一のオブジェクトではなく、モデルの複数化した属性名にはオブジェクトの配列を渡す必要があります。
以下は、has_and_belongs_to_many
を介して関連付けられた 2 つのモデルの例です。
FactoryGirl.define do # language factory with a `belongs_to` association for the profile factory :language do title "Through the Looking Glass" profile end # profile factory without associated languages factory :profile do name "John Doe" # profile_with_languages will create language data after the profile has # been created factory :profile_with_languages do # languages_count is declared as an ignored attribute and available in # attributes on the factory, as well as the callback via the evaluator transient do languages_count 5 end # the after(:create) yields two values; the profile instance itself and # the evaluator, which stores all values from the factory, including # ignored attributes; `create_list`'s second argument is the number of # records to create and we make sure the profile is associated properly # to the language after(:create) do |profile, evaluator| create_list(:language, evaluator.languages_count, profiles: ) end end endend
これにより、次のようなことが可能になります。 シーケンスは定義ブロック内でsequence
を呼び出して定義し、シーケンスの値はgenerate
を呼び出して生成されます。
factory :user do email # Same as `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
シーケンスにはエイリアスも用意されています。
エイリアスを定義し、カウンターにデフォルト値(1)を使用する
factory :user do sequence(:email, aliases: ) { |n| "person#{n}@example.com" }end
値の設定:
factory :user do sequence(:email, 'a', aliases: ) { |n| "person#{n}@example.com" }end
値は、#next
メソッドをサポートしていればよいのです。
Traits
Traits は、属性をグループ化し、任意のファクトリーに適用することができます。
また、サブクラスで trait が与える個々の属性をオーバーライドできます。
また、factory_girl からインスタンスを構築するときに、シンボルのリストとして trait を渡すことができます。
Traits は関連付けでも簡単に使用できます。
Factory と異なる関連付け名を使用している場合、
Traits は他の traits 内で使用でき、その属性を混在させることが可能です。
factory :order do trait :completed do completed_at { 3.days.ago } end trait :refunded do completed refunded_at { 1.day.ago } endend
最後に、trait は一時的な属性を受け入れることができます。
コールバック
factory_girl は、いくつかのコードを注入するための 4 つのコールバックを利用可能にします:
例:
±2612> ブロック内にユーザーのインスタンスを持つことに注意してください。 これは便利です。
また、同じファクトリーに複数の種類のコールバックを定義できます:
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
の両方のコールバックが呼び出されます。
また、標準属性と同様に、子ファクトリーは親ファクトリーのコールバックを継承し、定義できます。
複数のコールバックをブロックの実行に割り当てることができます。これは、同じコードを実行するさまざまな戦略を構築する場合に便利です (すべての戦略にわたって共有されるコールバックは存在しないため)。
すべてのファクトリーのコールバックをオーバーライドするには、FactoryGirl.define
ブロック内で定義します。
また、Symbol#to_proc
に依存するコールバックを呼び出すこともできます。
Gem がユーザー ファクトリーを提供する場合、
FactoryGirl.define do factory :user do full_name "John Doe" sequence(:username) { |n| "user#{n}" } password "password" endend
追加属性を追加する子ファクトリーを作成する代わりに、
そのファクトリーを変更できます。
FactoryGirl.modify do factory :user do full_name "Jane Doe" date_of_birth { 21.years.ago } gender "Female" health 90 endend
ファクトリーを変更する場合、(コールバック以外の)任意の属性を変更することができます。
FactoryGirl.modify
はファクトリーに対して異なる操作を行うため、FactoryGirl.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) # array of stubbed users
一度に2つのレコードを作成する *_pair
メソッドのセットもあります。
users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
Linting Factories
factory_girl は、既知のファクトリーのリンティングを可能にします:
FactoryGirl.lint
FactoryGirl.lint
各ファクトリーを作成し、作成プロセス中に発生する例外を捕捉します。 FactoryGirl::InvalidFactoryError
は、作成できなかったファクトリーのために、ファクトリーのリスト (と対応する例外) とともに発生します。
Rake タスクの例:
FactoryGirl.lint
を呼び出した後、レコードが作成される可能性が高いので、データベースをクリアしたいと思うかもしれません。 上記の提供された例では、データベースをクリアするために database_cleaner gem を使用しています。
lint させたいファクトリーだけを渡すことで、選択的にファクトリーを lint できます。 このオプションは、ファクトリーの各トレイトがそれ自身で有効なオブジェクトを生成することを確認します。これは、lint
メソッドに traits: true
を渡すことで有効になります:
FactoryGirl.lint traits: true
これは他の引数と組み合わせることもできます:
FactoryGirl.lint factories_to_lint, traits: true
linting に使用する戦略も指定することができます。
FactoryGirl.lint strategy: :build
Custom Construction
いくつかの属性が initialize
に渡されるオブジェクトを構築するために factory_girl を使用したい場合、または構築クラスで単に new
を呼び出す以外のことを行いたい場合、ファクトリーに initialize_with
を定義してデフォルト動作をオーバーライドすることが可能です。 例:
factory_girl は ActiveRecord で動作するように書かれていますが、任意の Ruby クラスで動作させることも可能です。 ActiveRecord との互換性を最大化するために、デフォルトのイニシャライザは、引数なしでビルド・クラス上で new
を呼び出してすべてのインスタンスをビルドします。 そして、属性ライター・メソッドを呼び出して、すべての属性値を割り当てます。 これは ActiveRecord ではうまくいくのですが、実は他のほとんどの Ruby クラスではうまくいきません。
イニシャライザをオーバーライドすることができます。
- 引数を必要とする ActiveRecord 以外のオブジェクトを
initialize
- インスタンスを生成するのに
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 5 end name "John Doe" initialize_with { new(attributes) }end
これにより、new
に渡されるすべての属性のハッシュが構築されます。 これは一時的な属性を含みませんが、ファクトリーで定義された他のすべての属性が渡されます (関連付け、評価済みシーケンス、など)。)
FactoryGirl.define
ブロックに含めることにより、すべてのファクトリーで initialize_with
を定義できます。
FactoryGirl.define do initialize_with { new("Awesome first argument") }end
initialize_with
ブロックを使用すると、initialize_with
ブロック内からアクセスされる属性はコンストラクターでのみ割り当てられます。
カスタム戦略
カスタムの構築戦略を追加することによって factory_girl の動作を拡張したい場合があるかもしれません。 association
とresult
です。 association
はFactoryGirl::FactoryRunner
インスタンスを受け取り、必要に応じてrun
を呼び出し、戦略をオーバーライドすることができます。 2番目のメソッドであるresult
はFactoryGirl::Evaluation
インスタンスを受け取る。 (notify
による) コールバックのトリガー、object
または hash
(factory で定義された属性に基づく結果インスタンスまたはハッシュを取得)、および create
は factory で定義された to_create
コールバックを実行します。
factory_girl によるストラテジの内部での使用方法を理解するには、おそらく 4 つのデフォルトストラテジのそれぞれのソースだけを見ることが最も簡単でしょう。
以下は、モデルの JSON 表現を構築するためにFactoryGirl::Strategy::Create
を使用してストラテジーを構成する例です。
Factory_girl が新しいストラテジーを認識できるように、それを登録します。
FactoryGirl.register_strategy(:json, JsonStrategy)
これにより、
FactoryGirl.json(:user)
最後に、必要であれば、ストラテジーに代わる新しいオブジェクトを登録して、factory_girl 自身のストラテジーを上書きすることができます。
オブジェクトを永続化するカスタム メソッド
デフォルトでは、レコードを作成すると、インスタンスの save!
が呼び出されますが、これは常に理想的とは言えないので、ファクトリーの to_create
で定義することによりその動作をオーバーライドできます。
factory :user_without_database do skip_createend
すべてのファクトリーで to_create
をオーバーライドするには、FactoryGirl.define
ブロック内で定義します:
FactoryGirl.define do to_create { |instance| instance.persist! } factory :user do name "John Doe" endend
ActiveSupport Instrumentation
どのファクトリーが作成され、どの構築戦略で実行するかを追跡するために、ActiveSupport::Notifications
は実行中のファクトリーを購読する方法を提供します。 1358>
もう 1 つの例は、すべてのファクトリーと、それらがテスト スイート全体でどのように使用されるかを追跡することです。 RSpec を使用している場合は、before(:suite)
と after(:suite)
を追加するだけです。
Rails プリローダーと RSpec
spring
や zeus
などの Rails プリローダーで RSpec を実行すると、以下のように関連付きのファクトリーを作成するときに ActiveRecord::AssociationTypeMismatch
エラーに遭遇する可能性があります。
テスト スイートの実行中にエラーが発生します。
可能な解決策は、プリローダーなしでスイートを実行するか、次のように RSpec 設定に FactoryGirl.reload
を追加するかの 2 つです。
RSpec.configure do |config| config.before(:suite) { FactoryGirl.reload }end
Bundler なしで使用する場合
Bundler を使用していない場合は、gem がインストールされていることを確認し、次のように呼び出します:
require 'factory_girl'
必要に応じて、spec/factories
または test/factories
のディレクトリ構造を持っているとすると、
FactoryGirl.find_definitions
Factory に別のディレクトリ構造を使っているなら、定義ファイルパスを変更してから定義を探そうとします。
FactoryGirl.definition_file_paths = %w(custom_factories_directory)FactoryGirl.find_definitions
ファクトリーの個別のディレクトリがなく、インラインで定義したい場合、それも可能です。