วิธีตั้งค่าโรงงานใน FactoryGirl ด้วย has_many สมาคม


90

ใครช่วยบอกหน่อยได้ไหมว่าฉันแค่ตั้งค่าผิดวิธี

ฉันมีโมเดลต่อไปนี้ที่มี has_many ผ่านการเชื่อมโยง:

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 นี่คือการตรวจสอบคุณภาพ rspec rspec พื้นฐานของฉันสำหรับ:listingโรงงาน:

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

นี่คือโรงงาน (: รายชื่อ)

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 คือมันไม่สมเหตุสมผลที่จะสร้างวัตถุโรงงานด้วยวิธีนี้หรือฉันพลาดอะไรไป? ขอบคุณมากสำหรับข้อมูลใด ๆ และทั้งหมด!

คำตอบ:


58

การสร้างการเชื่อมโยงประเภทนี้จำเป็นต้องใช้การเรียกกลับของ 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

คุณใช้การเชื่อมโยง: คุณลักษณะ [: factory =>: feature] หรือไม่
davidtingsu

110

หรือคุณสามารถใช้บล็อกและข้ามassociationคีย์เวิร์ดได้ สิ่งนี้ทำให้สามารถสร้างวัตถุได้โดยไม่ต้องบันทึกลงในฐานข้อมูล (มิฉะนั้นการเชื่อมโยง has_many จะบันทึกระเบียนของคุณไปยังฐานข้อมูลแม้ว่าคุณจะใช้buildฟังก์ชันแทนcreate)

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

5
นี่คือแมวเหมียว ความสามารถทั้งสองอย่างbuildและcreateทำให้เป็นรูปแบบที่หลากหลายที่สุด จากนั้นใช้นี้ FG กำหนดเองสร้างกลยุทธ์gist.github.com/Bartuz/74ee5834a36803d712b7ไปpost nested_attributes_forเมื่อการทดสอบการดำเนินการควบคุมที่accepts_nested_attributes_for
คริสเบ็ค

4
โหวตมากขึ้นอ่านง่ายและหลากหลายกว่าคำตอบที่ยอมรับ IMO
m_x

1
ตั้งแต่ FactoryBot 5 associationคำหลักจะใช้กลยุทธ์การสร้างเดียวกันสำหรับแม่และลูก ดังนั้นจึงสามารถสร้างวัตถุที่ไม่มีการบันทึกลงในฐานข้อมูล
นิค

30

คุณสามารถใช้trait:

FactoryGirl.define do
  factory :listing do
    ...

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

ด้วยcallbackถ้าคุณต้องการสร้างฐานข้อมูล:

...

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.