【问题标题】:Populating an association with children in factory_girl在 factory_girl 中填充与儿童的关联
【发布时间】:2011-02-25 14:39:30
【问题描述】:

我有一个模型Foo,它有_many 'Bar'。我为这些对象中的每一个都有一个 factory_girl 工厂。 Bar 的工厂与 Foo 有关联;它会在创建 Bar 时实例化一个 Foo。

我想要一个创建包含 Bar 的 Foo 的工厂。理想情况下,此 Bar 将通过 :bar 工厂创建,并尊重用于创建 Foo 的构建策略(创建/构建)。

我知道我可以调用 :bar 工厂,然后从新 Bar 中获取 Foo 引用。我想避免这种情况;在我的测试用例中,重要的对象是 Foo;调用酒吧工厂似乎有点迂回。此外,我可以看到需要具有多个 Bars 的 Foo。

这在 factory_girl 中可行吗?您如何在父级中定义这种关系?

【问题讨论】:

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


    【解决方案1】:

    您可以同时使用association 方法:

    Factory.define :foo do |f|
      # ...
      f.association :bar
    end
    

    如果这不起作用,您可以使用回调手动关联它们。这是我的一个应用程序中的一个示例:

    Factory.define :live_raid do |raid|
    end
    
    Factory.define :live_raid_with_attendee, :parent => :live_raid do |raid|
      raid.after_create { |r| Factory(:live_attendee, :live_raid => r) }
    end
    

    【讨论】:

    • 第一个语法在我的代码中导致堆栈溢出/无限递归。第二种语法效果很好,只是它不遵守构建策略。
    【解决方案2】:

    Factory.after_ hooks 似乎是成功执行此操作的唯一方法。我已经找到了一种无需重复代码即可维护构建策略的方法:

    Factory.define :foo do |f|
      f.name "A Foo"
      f.after(:build) { |foo|
        foo.bars << Factory.build(:bar, :foo => foo)
      }
      f.after(:create) { |foo|
        foo.bars.each { |bar| bar.save! }
      }
    end
    

    documentation 声明如果使用:create 构建策略,after_build 将在after_create 之前调用。如果使用:build,那么只调用after_build,大家就开心了。

    我还创建了一个抽象的通用版本at this gist 以保持干燥。

    【讨论】:

    • 很难相信这是最好的方法,但似乎仍然如此。对此答案投了赞成票。
    • 你如何使用你的 gist 版本?将它整合到 Rails 应用程序中的最佳方式是什么?
    • 如here 所述,使用&lt;&lt; 添加关联会保留更改。为避免这种情况,after(:build) 应为 foo.association(:bars).add_to_target(Factory.build(:bar, :foo =&gt; foo) )
    【解决方案3】:

    FactoryGirl 现在有一个可用于关联的:method =&gt; :build 选项,它将构建关联对象而不是创建它。

    #64: Building an object creates associations

    【讨论】:

      【解决方案4】:

      FactoryGirl 4.3.0 在父对象上调用 build 时在关联上调用 save!,我认为这不是正确的行为。

      在挖掘 FactoryGirl 代码后,将 strategy: :build 添加到工厂中的关联定义现在似乎无需调用 save! 即可创建我的关联。

      【讨论】:

        【解决方案5】:

        使用factory_girl-4.5.0,在父对象工厂中创建n个子对象

        FactoryGirl.define do
          factory :foo do
            name "test"        
        
            after(:build) do |instance|
              n.times { instance.bars << FactoryGirl.create(:bar) }          
            end
          end
        end
        

        【讨论】:

          猜你喜欢
          • 2012-11-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-10
          相关资源
          最近更新 更多