10

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

在尊重 factory_girl 的构建策略的同时,这似乎是这个问题的一个克隆,但在 factory_girl 发布后 2.5 年发生了很大变化。

我有一个名为 photos 的具有 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

无需说它必须存在更好的方法来做到这一点。

想法?

4

5 回答 5

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
于 2012-11-15T20:35:09.170 回答
9

这是 Flipstone 答案的稍微干净一点的版本:

factory :offering do
  trait :stay do
    ...
    photos do
      association :hotel_photo, :strategy => @build_strategy.class
    end
  end
end
于 2013-11-08T14:34:42.377 回答
3

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

factory :offering do
  trait :stay do
    ...
    photos do
      FactoryGirl::FactoryRunner.new(:hotel_photo, @build_strategy.class, []).run
    end
  end
end
于 2013-02-11T15:35:22.003 回答
1

其他答案有一个缺陷,反向关联没有被正确初始化,例如offering.photos.first.offering == offeringis falseoffering更糟糕的是,不正确,Offering对于每个photos.

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

为了克服流程并简化事情:

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

@instanceOffering是当前工厂创建的一个实例。对于好奇的,上下文是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将使用 a 访问数据库SELECT COUNT(*) FROM hotel_photos ...(在大多数情况下将返回 0),请在断言中使用lengthor 。size
于 2017-04-29T19:32:24.737 回答
0

这种事情对我有用:

factory :offering do
  trait :stay do
    ...
    photos { |o| [o.association(:hotel_photo)] }
  end
end
于 2014-04-08T20:13:24.837 回答