我有一个家庭类是这样定义的:
class Family < ActiveRecord::Base
after_initialize :initialize_family
belongs_to :user
validates :user,
:presence => true
validates :name,
:presence => true,
:length => { :maximum => 30 },
:format => { :with => /A[a-zA-Z0-9-_s']+z/i}
def initialize_family
if self.name.blank? && self.user
self.name = "#{self.user.profile_full_name}'s Family"
end
end
end
在我的工厂.rb中,我有:
Factory.define :family do |f|
f.association :user, :factory => :user
end
在我的family_spec.rb 中,我有
let(:family) { Factory(:family) }
但这失败了:
1) Family is valid with valid attributes
Failure/Error: let(:family) { Factory(:family) }
ActiveRecord::RecordInvalid:
Validation failed: Name can't be blank, Name is invalid, Languages can't be blank, Languages is too short (minimum is 1 characters)
# ./spec/models/family_spec.rb:8:in `block (2 levels) in <top (required)>'
# ./spec/models/family_spec.rb:10:in `block (2 levels) in <top (required)>'
使用调试器,我可以看到当after_initialize称为self.user时为零。为什么会这样?如果我打电话给家庭创建或新,一切正常。
感谢您的任何帮助。
从Joe Ferris那里得到的答案:
factory_girl 不会将参数传递给构造函数。它使用 #user= 上您的模型,并在没有任何参数的情况下实例化它。
还有本·休斯的这个:
为了详细说明乔所说的话,after_initialize方法被称为对象初始化后立即,并且该时间确实用户没有已设置。
因此,例如,虽然这将起作用:
family = Family.create!(:user => @user) # or @user.families.create ...
这不会(这就是factory_girl在引擎盖下所做的):
family = Family.new
family.user = @user
family.save!
一般来说,您希望真正小心地使用after_initialize,因为请记住,这是在每个对象初始化时调用的。一家人电话在 1,000 个对象上将导致它被调用 1,000 次。
听起来在这种情况下,您可能最好使用before_validation而不是after_initialize。
以下语法也适用于在 rspec 中进行测试:
let (:family) { Family.create(:user => @user) }
由于after_initialize
是在实例化新对象后触发的,并且默认情况下factory_girl通过调用new
而不带任何参数来构建实例,因此您必须使用 initialize_with
来覆盖默认构建。
FactoryGirl.define do
factory :family do
initialize_with { new(user: build(:user)) }
end
end
这是因为关联是懒惰的,因此在"after_initialize"中还没有用户。
http://rdoc.info/github/thoughtbot/factory_girl/v1.3.3/file/README.rdoc
也许你可以直接从一家工厂打电话给另一家工厂,但我没有尝试过这个,例如
f.user Factory(:user)