Cách thiết lập nhà máy trong FactoryGirl với liên kết has_many


89

Ai đó có thể cho tôi biết nếu tôi chỉ thiết lập sai cách không?

Tôi có các mô hình sau có liên kết 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

Tôi đang sử dụng Rails 3.1.rc4, FactoryGirl 2.0.2, factory_girl_rails 1.1.0 và rspec. Đây là kiểm tra độ tỉnh táo rspec rspec cơ bản của tôi cho :listingnhà máy:

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

Đây là Nhà máy (: danh sách)

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

Các nhà máy :listing_feature:featurenhà máy được thiết lập tương tự.
Nếu association :featuresdòng được nhận xét ra, thì tất cả các bài kiểm tra của tôi đều vượt qua.
Khi nó là

association :features, :factory => :feature

thông báo lỗi undefined method 'each' for #<Feature> mà tôi nghĩ có ý nghĩa với tôi bởi vì listing.featurestrả về một mảng. Vì vậy, tôi đã thay đổi nó thành

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

và lỗi tôi nhận được bây giờ là ArgumentError: Not registered: features Có phải tôi không hợp lý khi tạo các đối tượng của nhà máy theo cách này, hay tôi đang thiếu cái gì? Cảm ơn rất nhiều cho bất kỳ và tất cả đầu vào!

Câu trả lời:


57

Việc tạo các loại liên kết này yêu cầu sử dụng lệnh gọi lại của FactoryGirl.

Một tập hợp các ví dụ hoàn hảo có thể được tìm thấy ở đây.

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

Để đưa nó về nhà với ví dụ của bạn.

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

bạn đã kết thúc bằng cách sử dụng liên kết: tính năng, [: nhà máy =>: tính năng]?
davidtingsu

108

Ngoài ra, bạn có thể sử dụng một khối và bỏ qua associationtừ khóa. Điều này giúp bạn có thể xây dựng các đối tượng mà không cần lưu vào cơ sở dữ liệu (nếu không, một liên kết has_many sẽ lưu các bản ghi của bạn vào db, ngay cả khi bạn sử dụng buildhàm thay vì create).

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

5
Đây là tiếng kêu meo meo của con mèo. Khả năng cả hai buildcreatelàm cho nó trở thành mẫu linh hoạt nhất. Sau đó, sử dụng chiến lược xây dựng FG tùy chỉnh này gist.github.com/Bartuz/74ee5834a36803d712b7 để post nested_attributes_forkiểm tra các hành động của bộ điều khiểnaccepts_nested_attributes_for
Chris Beck

4
upvoted, xa dễ đọc hơn và linh hoạt hơn so với câu trả lời được chấp nhận IMO
m_x

1
Kể từ FactoryBot 5, associationtừ khóa sử dụng cùng một chiến lược xây dựng cho cấp độ gốc và cấp độ con. Vì vậy, nó có thể xây dựng các đối tượng được lưu vào cơ sở dữ liệu.
Nick

27

Bạn có thể sử dụng trait:

FactoryGirl.define do
  factory :listing do
    ...

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

Với callback, nếu bạn cần tạo DB:

...

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

Sử dụng trong thông số kỹ thuật của bạn như thế này:

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

Điều này sẽ loại bỏ sự trùng lặp trong nhà máy của bạn và có thể tái sử dụng nhiều hơn.

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


20

Tôi đã thử một vài cách tiếp cận khác nhau và đây là cách hoạt động đáng tin cậy nhất đối với tôi (điều chỉnh cho phù hợp với trường hợp của bạn)

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

Đây là cách tôi thiết lập:

# 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

Và sau đó trong các bài kiểm tra của mình, bạn có thể làm:

@preference_set = FactoryGirl.create(:preference_set_with_preferences)

Hy vọng rằng sẽ giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.