Reputation: 3422
I have a data model where account
has a unique validation on a subdomain.
Most of my other objects are associated with the account
object.
In my data model, a product
has an account_id
and a program_id
In FactoryBot I would like to write something like :
create(:product, :with_program, account: account)
Then to have all associated resources created with this particular account.
and in my product
factory :
FactoryBot.define do
factory :product do
association :account
association :program
trait :with_program do
after_build do |product|
product.program = create(
:program,
:with_author,
account: product.account
)
end
end
end
end
However this directly creates the program which in turn recreates an account them triggers the uniqueness validation on account.
FactoryBot.define do
factory :program do
association :account
end
end
So far the only solution I found is to create all associations product relies on before creating the actual product and passing them to the create method
:
let(:program) do
create(
:program,
:with_future_start_date_sessions,
account: account,
author: author
)
end
create(
:product,
:draft,
program: program,
account: account
)
end
Is there a smarter way to pass down the account
to all product
associations and create them in a trait
or something equivalent?
Upvotes: 1
Views: 3868
Reputation: 2891
I'm not sure if this helps, but I did something like this. I have various validations on the associated models and this way both build and create factories worked:
FactoryBot.define do
factory :product do
account(:build) do |product|
product.program ||= build(:program)
product.account ||= build(:account)
end
after(:create) do |product|
product.program.save!
product.account.save!
end
end
end
Upvotes: 0
Reputation: 1205
When creating complex associations I always prefer to do it with a top down approach. Instead of creating products with account, try creating an account with products, that way you wouldn't need to create associations before hand.
FactoryBot.define do
factory :account do
# account attributes...
trait :with_products do
transient do
product_count {2}
end
after(:build, :create) do |account, evaluator|
create_list(
:product,
evaluator.product_count,
:with_program,
account: account
)
end
end
end
end
Then you could create products by calling
# 1 account, 5 products
FactoryBot::create(:account, :with_products, product_count: 5)
# 5 accounts with 1 product each
FactoryBot::create_list(:account, 5, :with_products, product_count: 1)
Upvotes: 3