Factory Girl创建与现有对象的关联。

11

我是FactoryGirl的新手,正在尝试以下简单场景:

factory :female, :class => Gender do
  code 'Female'
end

factory :male, :class => Gender do
  code 'Male'
end

factory :papas, :class => Customer do
  first_name 'Jim'
  last_name 'Papas'
  association :gender, :factory => :male, :strategy => :build
end

factory :dumas, :class => Customer do
  first_name 'Mary'
  last_name 'Dumas'
  association :gender, :factory => :female, :strategy => :build
end

那么在我的测试中:

 create(:male)
 create(:female)
 create(:papas)
 create(:dumas)
请注意,Customer类与Gender类存在关联关系belongs_to,并且有一条验证规则,规定gender_id应该存在。我还在Gender类上设置了一条验证规则,要求它的code属性具有唯一性。
在上面的create(:papas)语句中,在我的测试中,我会收到一个错误消息,说将要创建的Customer无效,因为gender_idnil
如果我在与gender相关的Customer :papas工厂关联上移除:strategy => :build,那么我就会收到一个错误消息,说正在尝试创建:papas时,该gender的code已经存在。
我需要做什么才能使我的测试按照上述要求创建数据?
请注意,我希望在其他测试中也能创建没有顾客的性别。不要告诉我使用工厂创建命令来创建顾客,并同时让顾客创建各自的性别。如果我尝试创建两个相同性别的顾客,这种方法将行不通。
另外,一定有更好的答案。
@male = create(:male)
@female = create(:female)
create(:papas, :gender => @male)
create(:dumas, :gender => @female)

(当使用固定装置时,这些东西已经可以直接使用了。我是否应该回到使用固定装置的方式?)

3个回答

14

你可以通过不使用 Gender 实例的工厂并在 Customer 工厂中使用回调函数来实现你想要的效果。

从目前的情况来看,Gender 工厂并没有为你带来太多好处(尽管我理解这可能是为了提问而简化的)。你同样可以通过以下方式获取 Gender:

Gender.find_or_create_by_code!('Male')

然后在您的客户工厂中,您可以使用一个 before 回调函数来设置性别:

factory :dumas, :class => Customer do
  first_name 'Mary'
  last_name 'Dumas'
  before_create do |customer|
    customer.gender = Gender.find_or_create_by_code!('Female')
  end
end

在factory_girl中使用回调的格式最近已经更改,因此如果您使用的是3.3或更高版本,则需要执行以下操作:

before(:create) do |customer|

使用before_create代替。

如果创建性别比您的示例更复杂,您仍然可以使用工厂创建性别,但是在将它们与客户连接时,请使用回调函数使用find_by_code!而不是find_or_create_by_code!


尽管相对于固定数据,你的回答有些啰嗦,但是是正确的。我已经使用Factory Girl两个月了,但是我的下一个项目仍然会使用固定数据! - p.matsinopoulos
1
使用工厂的优点是,如果您需要创建许多不同类型的客户,则可以拥有男性客户和女性客户工厂,并将其用作不同客户的父工厂-这将大大减少冗余。很高兴它起作用了! - Shadwell

6

ignore已经不推荐使用,并且将在Factory Girl的5.0版本中被移除。

取而代之,你可以使用暂时属性(transient attributes)

factory :dumas, :class => Customer do
  transient do
    female { Gender.find_by_code('Female') || create(:female) }
  end

  first_name 'Mary'
  last_name 'Dumas'
  gender { female }
end

5
ignore块可以用来包装这个内容:
factory :dumas, :class => Customer do
  ignore do
    female { Gender.find_by_code('Female') || create(:female) }
  end

  first_name 'Mary'
  last_name 'Dumas'
  gender { female }
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接