2012-11-09 54 views
7

我的問題似乎很常見,但我還沒有在文檔或Internet本身找到任何答案。FactoryGirl:填充一個有很多關係的保存構建策略

它似乎是這個問題的一個克隆has_many while respecting build strategy in factory_girl,但在該工廠factory_girl改變很多後的2年之後。

我有一個與has_many關係稱爲照片的模型。我想填充這個有很多關係保留我的構建策略的選擇。

如果我打電話offering = FactoryGirl.build_stubbed :offering, :stay我希望offering.photos是一個殘段模型的集合。

我找到了實現這一目標的唯一途徑就是這一個:

factory :offering do 
    association :partner, factory: :named_partner 
    association :destination, factory: :geolocated_destination 

    trait :stay do 
    title "Hotel Gran Vía" 
    description "Great hotel in a great zone with great views" 
    offering_type 'stay' 
    price 65 
    rooms 70 
    stars 4 
    event_spaces 3 
    photos do 
     case @build_strategy 
     when FactoryGirl::Strategy::Create then [FactoryGirl.create(:hotel_photo)] 
     when FactoryGirl::Strategy::Build then [FactoryGirl.build(:hotel_photo)] 
     when FactoryGirl::Strategy::Stub then [FactoryGirl.build_stubbed(:hotel_photo)] 
     end 
    end 
    end 
end 

沒有必要說,它必須存在一個更好的方式做到這一點。

想法?

回答

8

這裏的Flipstone的回答稍微清潔的版本:

factory :offering do 
    trait :stay do 
    ... 
    photos do 
     association :hotel_photo, :strategy => @build_strategy.class 
    end 
    end 
end 
+0

在我看來這適用於不是has_many的關聯嗎? –

+0

我不認爲你需要在這裏指定策略。 –

+0

反向不適合我,例如'offering.photos.first.offering == offering'是'false'。 –

10

您可以使用各種FactoryGirl回調:

factory :offering do 
    association :partner, factory: :named_partner 
    association :destination, factory: :geolocated_destination 

    trait :stay do 
    title "Hotel Gran Vía" 
    description "Great hotel in a great zone with great views" 
    offering_type 'stay' 
    price 65 
    rooms 70 
    stars 4 
    event_spaces 3 
    after(:stub) do |offering| 
     offering.photos = [build_stubbed(:hotel_photo)] 
    end 
    after(:build) do |offering| 
     offering.photos = [build(:hotel_photo)] 
    end 
    after(:create) do |offering| 
     offering.photos = [create(:hotel_photo)] 
    end 
    end 
end 
+0

這的確是一種更好的方式。我發現像make(:hotel_photo)這樣的方法可以尊重你的偏好,但這似乎很好。接受 –

+1

謝謝!我用'send(hook,:thing)'刪除了一點重複。如果這可以在任何工廠的通用幫助程序中獲得,那將會很好。 –

+0

我已經創建了一個問題github揭露這個想法。 http://github.com/thoughtbot/factory_girl/issues/458 –

3

您也可以直接調用FactoryRunner類,並把它傳遞給使用構造策略。

factory :offering do 
    trait :stay do 
    ... 
    photos do 
     FactoryGirl::FactoryRunner.new(:hotel_photo, @build_strategy.class, []).run 
    end 
    end 
end 
0

這種事情對我的作品:

factory :offering do 
    trait :stay do 
    ... 
    photos { |o| [o.association(:hotel_photo)] } 
    end 
end 
1

其他的答案有一個缺陷,逆關聯不是正確初始化,例如offering.photos.first.offering == offeringfalse。更糟的是,這是不正確的,offeringphotos中的每一個新的Offering

此外,明確指定策略是多餘的。

爲了克服流動,把事情簡單化:

factory :offering do 
    trait :stay do 
    ... 
    photos do 
     association :hotel_photo, offering: @instance 
    end 
    end 
end 

@instance是工廠目前正在創建的Offering的一個實例。對於好奇,上下文是FactoryGirl::Evaluator

如果你不喜歡@instance像我這樣做,你可能看在evaluator.rb,找到以下內容:

def method_missing(method_name, *args, &block) 
    if @instance.respond_to?(method_name) 
    @instance.send(method_name, *args, &block) 

我真的很喜歡如何itself外觀:

factory :offering do 
    trait :stay do 
    ... 
    photos do 
     association :hotel_photo, offering: itself 
    end 
    end 
end 

難道能使用itself,取消定義Evaluator

FactoryGirl::Evaluator.class_eval { undef_method :itself } 

它將被傳遞到@instance並且將返回@instance本身。

用於提供與幾張照片一個完整的例子的緣故:

factory :offering do 
    trait :stay do 
    ... 
    photos do 
     3.times.map do 
     association :hotel_photo, offering: itself 
     end 
    end 
    end 
end 

用法:

offering = FactoryGirl.build_stubbed :offering, :stay 
offering.photos.length # => 3 
offering.photos.all? { |photo| photo.offering == offering } # => true 

要小心,因爲預期有些事情可能無法正常工作:

  • offering.photos.first.offering_id將出人意料地是nil;
  • offering.photos.count將使用SELECT COUNT(*) FROM hotel_photos ...(並且在大多數情況下將返回0)命中數據庫,請在斷言中使用lengthsize