thoughtbot / thoughtbot/factory_bot
Forwarding from one factory to another
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 8.2k
- Forks
- 2.6k
- PR merge metrics
- No merged PRs in 30d
Description
Hey there, I'm having difficulty making one factory that forwards to another. I don't have a particular solution approach in mind, so I didn't use the "feature request" template. For all I know, this is already possible, and I just don't know how.
Background:
I have two related types, Foo, and FooRecord. FooRecord is part of our persistence-layer, similar to an active record model. Foo is a wrapper that encapsulates a FooRecord. The goal is for Foo to make business logic be database-agnostic, so that we can swap out the underlying record implementation without external disruption.
We have a factory for Foo, which is used in most places, especially in our business layer.
We also have a factory for FooRecord, which is used in tests of our persistence-layer.
Our attempt
Here's what our FooRecord factory looks like factories look roughly like this:
FactoryBot.define do
factory :foo_record, class: FooRecord do
initialize_with { new(**attributes) }
transient do
_title { Faker::Commerce.product_name }
_subtitle { Faker::Commerce.color }
end
sequence(:id)
title { _title }
subtitle { _subtitle }
image_url { Faker::Internet.url(path: "/#{_product_title.parameterize}/#{_variant_title.parameterize}/image.jpg") }
# 10+ more fields are set here ...
trait :not_yet_loaded do
title { nil }
subtitle { nil }
image_url { nil }
# ...
end
end
end
As you can see, there are some parts of the FooRecord factory that are useful for the Foo factory to use:
- The fake
image_urlwe generate is more realistic than an entirely random URL, because it's that's based off thetitleandsubtitle - We have an index that ensures
FooRecordIDs are unique. We want this to be the case, regardless of whether you callcreate(:foo_record)orcreate(:foo). We want them to share the same ID so that the records can never collide, regardless of their origin. - We have a lot more fields, which invoke
Fakerin various ways. We don't want to duplicate this logic between the two factories. - We have traits such as
#not_yet_loaded, which we'd like to be able to use on both factories.
We tried to define a factory for Foo which forwards onto FooRecord, to piggy off all this existing functionality that it has:
FactoryBot.define do
factory :foo, class: Foo do
initialize_with do
new(foo_record: build(:foo_record, **attributes))
end
end
end
But as you can guess, this definition doesn't allow us to forward along traits.
Is there a way to achieve my goals in a nice clean way?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue provides no repository file or test entry point; begin with the shown Foo and FooRecord FactoryBot definitions, especially initialize_with, traits, attributes, and the shared ID sequence. Review how factory definitions and traits are represented, then determine whether forwarding can preserve the listed behavior; done means the requested factory relationship works without duplicating fields or traits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- ruby
- Domain
- testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100