FactoryGirl:填充有很多关系保存构建策略

我的问题似乎很常见,但我在文档或互联网本身没有找到任何答案。

它似乎是这个问题的一个克隆has_many同时尊重factory_girl中的构建策略,但是在post-factory_girl改变了很多之后的2。5年。

我有一个名为照片的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 

没必要说IT必须存在更好的方法。

想法?

这是一个稍微清洁的Flipstone版本的答案:

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

您可以使用各种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 

您还可以直接调用FactoryRunner类并将其传递给构建策略以供使用。

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

其他答案有一个缺陷,反向关联没有正确初始化,例如offering.photos.first.offering == offeringfalse 。 更糟糕的是,不正确,该offering是每张photos的新Offering

此外,明确指定策略是多余的。

要克服流程并简化事情:

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

@instance是工厂目前创建的Offering实例。 对于好奇的,上下文是FactoryGirl::Evaluator

如果您不像我这样喜欢@instance ,您可以查看@instance并找到以下内容:

 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 SELECT COUNT(*) FROM hotel_photos ...命中数据库,请在断言中使用lengthsize

这种事对我有用:

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