thoughtbot / thoughtbot/factory_bot
Question/Feature - Access `traits` and `overrides` arguments in public API
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 8.2k
- Forks
- 2.6k
- PR merge metrics
- No merged PRs in 30d
Description
Problem this feature will solve
I'd like to safely bypass the creation of a factory when no overrides or traits were specified. Ultimately, the idea is to reduce the number of records created after a test setup by defaulting to a fixture when no special attributes are required.
Note: This problem may already have been solved, and I need to learn about the solution. See the "Additional Context" section for questions.
Something along these lines:
factory :user do
to_create do |record, evaluator|
if evaluator.traits.empty? && evaluator.overrides.empty?
User.find(1) # users(:one)
else
record.save!
end
end
sequence(:name) { |n| 'Name #{n}' }
end
The formats could fit exactly the ones used in FactoryBot::FactoryRunner initialization
FactoryBot.create(:user)
=> overrides: {}, traits: []
FactoryBot.create(:user, name: 'Alex')
=> overrides: {name: 'Alex'}, traits: []
FactoryBot.create(:user, :with_password, name: 'Alex')
=> overrides: {name: 'Alex'}, traits: [:with_password]
Desired solution
It seems that exposing these methods to the evaluator could be a solution?
Note: Naming can change as the evaluator already has @overrides variable, but unless mistaken is for all the record attributes.
Alternatives considered
Custom FactoryBot::FactoryRunner with hook?
I can access these exact values by monkey patching FactoryBot::FactoryRunner which doesn't feel right.
module FactoryBot
class FactoryRunner
alias old_run run
def run(runner_strategy = @strategy, &block)
if Strategy::Create === @strategy && @traits.empty? && @overrides.empty?
record = case @name
when :user then User.find(1) # users(:one)
end
return record if record
end
old_run(runner_strategy, &block)
end
end
end
The FactoryRunner could be registered/injected like strategies with some hooks?
Additional context
I guess it relates to some extent to #1681
Questions
- Is there a way to access these values safely and not monkey patch or tinker with private APIs?
- Is there an existing approach to prevent the creation of a record when no arguments are passed other than the name of the factory?
Any input would be appreciated about that issue, except maybe feedback about how bad the idea is; I already know that 😂
The number of records created in specs with factories is known to have issues.
Any change helping developers with this issue would be significant, especially when dealing with legacy codebases.
EDIT: Maybe using a custom strategy and redefining the #association would be good enough as often the number of records created are from linked associations.
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
Start by reading the FactoryBot::FactoryRunner initialization and the evaluator APIs mentioned in the issue, then compare the questions with issue #1681. Review the custom strategy and #association alternatives described here. Done means establishing whether a supported public API already exists or defining the requested behavior and its API boundaries.
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