has_manyアソシエーションでFactoryGirlにファクトリーをセットアップする方法


88

私がセットアップを間違った方法で行っているのなら誰かが教えてもらえますか?

has_many.through関連付けを持つ次のモデルがあります。

class Listing < ActiveRecord::Base
  attr_accessible ... 

  has_many :listing_features
  has_many :features, :through => :listing_features

  validates_presence_of ...
  ...  
end


class Feature < ActiveRecord::Base
  attr_accessible ...

  validates_presence_of ...
  validates_uniqueness_of ...

  has_many :listing_features
  has_many :listings, :through => :listing_features
end


class ListingFeature < ActiveRecord::Base
  attr_accessible :feature_id, :listing_id

  belongs_to :feature  
  belongs_to :listing
end

Rails 3.1.rc4、FactoryGirl 2.0.2、factory_girl_rails 1.1.0、およびrspecを使用しています。これは、:listing工場の基本的なrspec rspec健全性チェックです。

it "creates a valid listing from factory" do
  Factory(:listing).should be_valid
end

これがFactory(:listing)です

FactoryGirl.define do
  factory :listing do
    headline    'headline'
    home_desc   'this is the home description'
    association :user, :factory => :user
    association :layout, :factory => :layout
    association :features, :factory => :feature
  end
end

:listing_featureそして:feature工場も同様に設定されています。行がコメント化されている
場合、association :features私のテストはすべて合格です。
あるとき

association :features, :factory => :feature

エラーメッセージは 、配列を返すundefined method 'each' for #<Feature> ため、私にはわかりやすいと思いlisting.featuresます。だから私はそれを

association :features, [:factory => :feature]

そして今私が得るエラーはArgumentError: Not registered: features 、このようにファクトリオブジェクトを生成することは賢明ではないのですか、それとも私は何が欠けているのですか?すべての入力をありがとうございました!

回答:


56

これらの種類の関連付けを作成するには、FactoryGirlのコールバックを使用する必要があります。

完璧な例のセットはここにあります。

https://thoughtbot.com/blog/aint-no-calla-back-girl

それをあなたの例に戻すために。

Factory.define :listing_with_features, :parent => :listing do |listing|
  listing.after_create { |l| Factory(:feature, :listing => l)  }
  #or some for loop to generate X features
end

関連付け:features、[:factory =>:feature]を使用してしまいましたか?
davidtingsu 2013年

106

または、ブロックを使用してassociationキーワードをスキップすることもできます。これにより、データベースに保存せずにオブジェクトを構築できます(そうでない場合、has_manyアソシエーションは、のbuild代わりに関数を使用しても、レコードをデータベースに保存しますcreate)。

FactoryGirl.define do
  factory :listing_with_features, :parent => :listing do |listing|
    features { build_list :feature, 3 }
  end
end

5
これは猫のニャーです。両方の能力buildcreateそれ最も汎用性の高いパターンになります。次に、このカスタムFGビルド戦略gist.github.com/Bartuz/74ee5834a36803d712b7を使用して、post nested_attributes_forコントローラーアクションをテストするときaccepts_nested_attributes_for
Chris Beck

4
はるかに読みやすく、汎用性の受け入れ答えよりもIMO、upvoted
m_x

1
FactoryBot 5以降、associationキーワードは親と子に同じビルド戦略を使用します。したがって、データベースに保存せずにオブジェクトを構築できます。
Nick

25

あなたが使うことができますtrait

FactoryGirl.define do
  factory :listing do
    ...

    trait :with_features do
      features { build_list :feature, 3 }
    end
  end
end

callback、DBの作成が必要な場合:

...

trait :with_features do
  after(:create) do |listing|
    create_list(:feature, 3, listing: listing)
  end
end

次のような仕様で使用します。

let(:listing) { create(:listing, :with_features) }

これにより、工場での重複がなくなり、再利用性が高まります。

https://robots.thoughtbot.com/remove-duplication-with-factorygirls-traits


20

私はいくつかの異なるアプローチを試しましたが、これは私にとって最も信頼できる方法でした(あなたのケースに適応しました)

FactoryGirl.define do
  factory :user do
    # some details
  end

  factory :layout do
    # some details
  end

  factory :feature do
    # some details
  end

  factory :listing do
    headline    'headline'
    home_desc   'this is the home description'
    association :user, factory: :user
    association :layout, factory: :layout
    after(:create) do |liztng|
      FactoryGirl.create_list(:feature, 1, listing: liztng)
    end
  end
end

0

これが私の設定方法です:

# Model 1 PreferenceSet
class PreferenceSet < ActiveRecord::Base
  belongs_to :user
  has_many :preferences, dependent: :destroy
end

#Model 2 Preference

class Preference < ActiveRecord::Base    
  belongs_to :preference_set
end



# factories/preference_set.rb

FactoryGirl.define do
  factory :preference_set do
    user factory: :user
    filter_name "market, filter_structure"

    factory :preference_set_with_preferences do
      after(:create) do |preference|
        create(:preference, preference_set: preference)
        create(:filter_structure_preference, preference_set: preference)
      end
    end
  end

end

# factories/preference.rb

FactoryGirl.define do
  factory :preference do |p|
    filter_name "market"
    filter_value "12"
  end

  factory :filter_structure_preference, parent: :preference do
    filter_name "structure"
    filter_value "7"
  end
end

そして、あなたのテストではあなたがすることができます:

@preference_set = FactoryGirl.create(:preference_set_with_preferences)

お役に立てば幸いです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.