FactoryGirl与多个模型的复杂关联

时间:2012-10-24 01:55:36

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

我正在试图弄清楚如何编写属于2个不同模型的工厂,每个模型应该具有相同的父模型。这是设计的示例代码:

class User < ActiveRecord::Base
  has_many :widgets
  has_many :suppliers

  attr_accessible :username
end

class Widget < ActiveRecord::Base
  belongs_to :user
  has_many :parts

  attr_accessible :name
end

class Supplier < ActiveRecord::Base
  belongs_to :user
  has_many :parts

  attr_accessible :name
end

class Part < ActiveRecord::Base
  belongs_to :supplier
  belongs_to :widget

  attr_accessible :name
end

这是我到目前为止所拥有的:

factory :user do
  name 'foo'
end

factory :widget do
  association :user
  name 'widget'
end

factory :supplier do
  association :user
  name 'supplier'
end

factory :part do
  association :widget
  association :supplier
  name 'part'
end

问题在于part.widget.user != part.supplier.user并且它们必须相同。

我尝试过以下方法但没有成功:

factory :part do
  association :widget
  association :supplier, user: widget.user
  name 'part'
end

有什么建议吗?或者在创建零件后我是否需要修改它?

谢谢

2 个答案:

答案 0 :(得分:8)

我相信你可以用callback

来做到这一点
factory :part do
  association :widget
  association :supplier
  name 'part'
  after(:create) do |part|
    user = FactoryGirl.create(:user)
    part.widget.user = part.supplier.user = user
  end
end

另请参阅:Get two associations within a Factory to share another association

答案 1 :(得分:0)

另一种选择是使用瞬态变量来传递相关对象。

我通常使用两个变量:

  • 用于保存工厂内使用的关联的变量
  • 一个布尔变量,用于指示是否为关联变量生成默认值 - 在您的特定情况下可能不需要,但可能非常有用

它的外观如下:

factory :part do
  transient do
    # this variable is so we can specify the user
    with_user { no_user ? nil : Factory.create(:user) }

    # this variable allows the user to be nil
    no_user false 
  end

  # The transient variable for_user can now be used to create the 
  # associations for this factory
  widget { Factory.create(:widget, :user => with_user) }
  supplier { Factory.create(:supplier, :user => with_user) }

  name 'part'
end

然后可以通过以下方式使用它:

# use the default user
part = Factory.create :part
part.widget.user.should == part.supplier.user

# use a custom created user
user = Factory.create :user, :name => 'Custom user'
part = Factory.create :part, for_user: user
part.widget.user.should == user
part.supplier.user.should == user

# create a part without any user
# (again this probably isn't need in your specific case, but I have 
#  found it a useful pattern)
part = Factory.create :part, no_user: true
part.widget.user.should be_nil
part.supplier.user.should be_nil