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

时间:2012-11-09 13:13:07

标签: ruby-on-rails testing factory-bot

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

这似乎是这个问题has_many while respecting build strategy in 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

无需说IT必须存在更好的方法。

想法?

5 个答案:

答案 0 :(得分: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

答案 1 :(得分:9)

以下是Flipstone答案的稍微清洁版本:

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:1)

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

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

克服流程并简化事情:

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

答案 4 :(得分:0)

这种事对我有用:

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