thoughtbot / thoughtbot/factory_bot

Allow overriding of `FactoryBot.automatically_define_enum_traits` on a per-factory basis

Open
#1,597 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

feature
Dominant language
Ruby
Stars
8.2k
Forks
2.6k
PR merge metrics
No merged PRs in 30d

Description

Problem this feature will solve

Having FactoryBot.automatically_define_enum_traits default to true is useful when using ActiveRecord::Enum to get up and running with factories quickly. However this is an all-or-nothing option for an entire codebase, with no way to override this on a per-factory basis where we need to customise, or deviate, from the automatic enum trait behaviour.

This is problematic when given a large codebase, with lots of ActiveRecord::Enum usage, as disabling this globally would mean re-working a lot of factories that already work perfectly fine, they would need to be updated to use traits_for_enum instead.

It would be great if we could allow a single factory to stop the automatic definition of enum's from happening, or vice versa if FactoryBot.automatically_define_enum_traits was false and a factory wanted to automatically define them.

Desired solution

If there was an additional option when defining a factory, it would allow us to opt-in, or opt-out of the global automatic enum trait definition behaviour. Something like the following when defining a factory:

FactoryBot.define do
  factory :task, automatically_define_enum_traits: false do
    # Normal FactoryBot scoping, only with no automatically defined enum traits just for this factory.
  end
end

Alternatives considered

Additional context

We have a monolithic codebase, with lots of models and lots of uses of ActiveRecord::Enum across various models. However 1 model has a complex Single Table Inheritance model that uses ActiveRecord::Enum. One of the types of this model doesn't work with all values of the enum. Which in turn doesn't work well with the global default of automatically defining enum traits. I've added a contrived example below as a demonstration, for the sake of a worked example, let's call this Task.

A Task has an ActiveRecord::Enum attribute of confirmed_by that uses the prefix option. On top of the prefixing, when the enums value is :user, it doesn't require extra metadata. When the enums value is :organisation, a validation kicks in to ensure extra metadata is populated to be considered valid.

FactoryBot doesn't seem to take prefixing into account when creating the enum's, it just uses the enums values. However, even if prefixing was supported, we don't refer to this within the tests as :confirmed_by_organisation, we refer to them as :org_confirmed. If FactoryBot supported an enum's :prefix and :suffix options, it would be helpful here, however we want to define our own set of traits that make more sense from this models perspective.

It would be great to have an additional config option, or something similar, when defining a complex factory. E.g. given the following contrived example model:

class Task
  enum :confirmed_by, [:user, :organisation], prefix: true
  validates :additional_metadata_for_organisation, presence: true, if: :confirmed_by_organisation?
end

If we could disable enum traits just for this factory, we could have full control over the defined traits. E.g.

FactoryBot.define do
  factory :task, automatically_define_enum_traits: false do
    confirmed_by { :user }
    
    trait :org_confirmed do
      confirmed_by { :organisation }
      organisation_metadata { { some_data: "with a value" } }
    end
  end
end

Without this option, the above factory would have been automatically defined with the following traits, of which the :organisation trait wouldn't pass linting, as it doesn't pass validation. This is fine in itself, however to fix it, we would need to redefine the :organisation trait to add in the extra attributes, when what we really want to do here is completely customise the traits. E.g.

FactoryBot.define do
  factory :task, automatically_define_enum_traits: false do
    trait :user do  # Ideally we don't want/need this trait
      confirmed_by { :user }
    end
    
    trait :organisation do  # Ideally we would name this trait something else if we could opt-out of automatically_define_enum_traits
      confirmed_by { :organisation }
      organisation_metadata { { some_data: "with a value" } }
    end
  end
end

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at FactoryBot.define and trace the existing automatically_define_enum_traits configuration path to understand how factory options and global settings are resolved. Done means a factory can opt in or out independently, while the global default continues to work and the behavior is covered for both settings.

Written by the indexing model from the issue text.

Assessment

Tech stack
rails, ruby
Domain
testing
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.