factory_bot
                                
                                 factory_bot copied to clipboard
                                
                                    factory_bot copied to clipboard
                            
                            
                            
                        Forwarding from one factory to another
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?
I've seen some people use an abstract (i.e. not meant to be instantiated) parent factory to solve this. Something like:
factory :foo_base, class: Object do
  # defines all the common stuff
end
factory :foo_record, class: FooRecord, parent: :foo_base do
  # anything specific to foo_record
end
factory :foo, class: Foo, parent: :foo_base do
  # anything specific to foo
end
There is some relevant discussion about this in https://github.com/thoughtbot/factory_bot/issues/1409
We may explore adding something like factory :foo_base, abstract: true to make this feature official, but I'm trying to gauge how common a use case it is. We did a little spike in https://github.com/thoughtbot/factory_bot/compare/abstract-parent-factories?expand=1 We'd probably also want to raise if somebody tried to actually build an abstract factory.
Oh that's an interesting approach. I'll try playing around with it
Looks like there was also some related conversation in https://github.com/thoughtbot/factory_bot/issues/985