Ruby on rails FactoryGirl:Populate a有许多保持关系的构建策略

Ruby on rails FactoryGirl:Populate a有许多保持关系的构建策略,ruby-on-rails,testing,factory-bot,Ruby On Rails,Testing,Factory Bot,我的问题似乎很常见,但我在文档或互联网本身中没有找到任何答案 这似乎是这个问题的复制品,但2,5年后,那个后工厂女孩改变了很多 我有一个模型,有很多关系,叫做照片。我想填充它,这与我选择的构建策略有很多关系 如果我调用offering=FactoryGirl.build\u stubbed:offering,:stay我希望offering.photos是stubbed模型的集合 我发现实现这一目标的唯一方法是: factory :offering do association :partn

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

这似乎是这个问题的复制品,但2,5年后,那个后工厂女孩改变了很多

我有一个模型,有很多关系,叫做照片。我想填充它,这与我选择的构建策略有很多关系

如果我调用
offering=FactoryGirl.build\u stubbed:offering,:stay
我希望
offering.photos
是stubbed模型的集合

我发现实现这一目标的唯一方法是:

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
没有必要说,它必须存在一个更好的方式来做到这一点


想法?

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

下面是Flipstone答案的一个稍微简洁的版本:

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

这种事情对我来说很有用:

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

其他答案存在缺陷,反向关联未正确初始化,例如,
offering.photos.first.offering==offering
false
。更糟糕的是,
产品
对于每个
照片
都是一个新的
产品

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

要克服流程并简化事情:

factory :offering do
  trait :stay do
    ...
    photos do
      association :hotel_photo, offering: @instance
    end
  end
end
@instance
是工厂目前正在创建的
产品的一个实例。出于好奇,上下文是
FactoryGirl::Evaluator

如果您不象我一样喜欢
@实例
,您可以在
evaluator.rb中查找以下内容:

def method_missing(method_name, *args, &block)
  if @instance.respond_to?(method_name)
    @instance.send(method_name, *args, &block)
我真的很喜欢
本身的样子:

factory :offering do
  trait :stay do
    ...
    photos do
      association :hotel_photo, offering: itself
    end
  end
end
一定要能够使用
本身
,在
计算器上取消定义它

FactoryGirl::Evaluator.class_eval { undef_method :itself }
它将被传递到
@实例
,并将返回
@实例
本身

为了提供包含多张照片的完整示例:

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(*)从酒店照片中点击数据库…
    (在大多数情况下将返回0),请在断言中使用
    length
    size

事实上,这是一种更好的方式。我正在检查make(:hotel_photo)这样的方法是否存在,它尊重您的偏好,但这似乎很好。接受谢谢!我用
send(hook,:thing)
删除了一点重复。如果这在任何工厂的通用助手中都可用,那就太好了。我已经创建了一个问题github来公开这个想法<代码>在(:stub)do | offering | offering.photos=[build_stubbed(:hotel_photo)]结束后
这不起作用。FactoryGirl引发错误:不允许存根模型访问database@Jared嗯,你用的是哪个版本的工厂女孩?另外,您是否有可能提供一个指向要点的链接,并复制该例外?在我看来,这适用于不太多的关联?我认为您不需要在这里指定策略。反向不适用于我,例如
offering.photos.first.offering==offering
false