Проблема с Factory_girl, ассоциацией и after_initialize
У меня есть класс семейства, определенный так:
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} 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, это nil. Почему это происходит?
Если я позвоню семье с созданием или новым, все будет хорошо работать.
Спасибо за любую помощь.
Ответы
Ответ 1
Это ответ, который я получил от Джо Ферриса:
factory_girl не передает аргументы конструктору. Он использует # user = on
вашей модели, и создает его без каких-либо аргументов.
и этот от Бена Хьюза:
Чтобы уточнить, что говорит Джо, после того, как вызывается метод инициализации
сразу после инициализации объекта, и это время действительно пользователь не имеет
был установлен.
Так, например, пока это будет работать:
family = Family.create!(:user => @user) # or @user.families.create ...
Это не будет (это то, что factory_girl делает под капотом):
family = Family.new
family.user = @user
family.save!
В общем, вы хотите быть очень осторожным, используя after_initialize, так как
помните, что это называется для каждой инициализации объекта. Семейный вызов
на 1000 объектов вызовет вызов 1000 раз.
Похоже, в этом случае вам может быть лучше использовать
before_validation вместо after_initialize.
Следующий синтаксис также работает для тестирования в rspec:
let (:family) { Family.create(:user => @user) }
Ответ 2
Так как after_initialize
запускается после создания новых объектов, а factory_girl создает экземпляры, вызывая new
без каких-либо аргументов по умолчанию, вы должны использовать initialize_with
для перезаписать сборку по умолчанию.
FactoryGirl.define do
factory :family do
initialize_with { new(user: build(:user)) }
end
end
Ответ 3
Я считаю, что это потому, что ассоциация ленива, поэтому в "after_initialize" пока нет пользователя.
http://rdoc.info/github/thoughtbot/factory_girl/v1.3.3/file/README.rdoc
Возможно, вы можете напрямую вызвать один factory из другого, но я не пробовал это, например.
f.user Factory(:user)