#ruby-on-rails #validation #factory-bot
#ruby-on-rails #проверка #factory-бот
Вопрос:
У меня есть модель учетной записи, которая имеет модель пользователя has_one, и модель пользователя, которая принадлежит модели учетной записи belongs_to. Я думаю, что базовый код, необходимый для демонстрации, это:
class Account < ActiveRecord::Base
has_one :user
validates_presence_of :user
accepts_nested_attributes_for :user
end
class User < ActiveRecord::Base
belongs_to :account
# validates_presence_of :account # this is not actually present,
# but is implied by a not null requirement
# in the database, so it only takes effect on
# save or update, instead of on #valid?
end
Когда я определяю ассоциации на каждой фабрике:
Factory.define :user do |f|
f.association :account
end
Factory.define :account do |f|
f.association :user
end
Я получаю переполнение стека, поскольку каждая из них рекурсивно создает учетную запись / пользователя.
Способ, которым я смог решить эту проблему, заключается в эмуляции вложенных форм атрибутов в моих тестах:
before :each do
account_attributes = Factory.attributes_for :account
account_attributes[:user_attributes] = Factory.attributes_for :user
@account = Account.new(account_attributes)
end
Однако я хотел бы сохранить эту логику на фабрике, поскольку она может выйти из-под контроля, как только я начну добавлять другие модули:
before :each do
account_attributes = Factory.attributes_for :account
account_attributes[:user_attributes] = Factory.attributes_for :user
account_attributes[:user_attributes][:profile_attributes] = Factory.attributes_for :profile
account_attributes[:payment_profile_attributes] = Factory.attributes_for :payment_profile
account_attributes[:subscription_attributes] = Factory.attributes_for :subscription
@account = Account.new(account_attributes)
end
Пожалуйста, помогите!
Ответ №1:
Я смог решить эту проблему, используя обратный вызов factory_girl after_build.
Factory.define :account do |f|
f.after_build do |account|
account.user ||= Factory.build(:user, :account => account)
account.payment_profile ||= Factory.build(:payment_profile, :account => account)
account.subscription ||= Factory.build(:subscription, :account => account)
end
end
Factory.define :user do |f|
f.after_build do |user|
user.account ||= Factory.build(:account, :user => user)
user.profile ||= Factory.build(:profile, :user => user)
end
end
Это создаст связанные классы до того, как класс-владелец будет сохранен, поэтому проверки пройдут.
Комментарии:
1. откуда
user.profile
взялся? Это подразумеваетсяuser.account
?2. user.profile — это модель, которая хранит информацию о пользователе, связанном с учетной записью. Итак, структура — Учетная запись> Пользователь> Профиль. Однако я добавил user.account, поскольку считаю, что это необходимо при непосредственном создании пользователя.
Ответ №2:
Взгляните на документацию factory_girl. Способ, которым вы создаете эти учетные записи, кажется, что вы на самом деле не используете преимущества factory_girl.
Я всегда заботился об ассоциациях, создавая нужные мне объекты перед тестированием. Я собираюсь попробовать это на основе моделей, на которые вы ссылаетесь выше:
before :each do
@account = Factory(:account, :user_id => Factory(:user).id, :profile_id => Factory(:profile).id)
end
Теперь @account
будут иметь @account.user
и @account.profile
доступны. Если вам нужно их определить, @profile = @account.profile
работает просто отлично.
Комментарии:
1. Проблема, с которой я сталкиваюсь, заключается в том, что user belongs_to account а не наоборот, поэтому учетная запись #user_id не существует, а User #account_id — это атрибут, который необходимо установить. Однако я не могу сохранить учетную запись для получения идентификатора без привязки учетной записи к соответствующему пользователю.