Use when tests need typed factories beyond plain Faker values: FactoryBot for Ruby (traits, associations), Mimesis for Python bulk data, Bogus for .NET Faker<T> builders, or a decision on which generator fits. Shows the same fixture in each tool; faker-data stays the default for simple values.
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Reference detail for synthetic-data-toolkit. FactoryBot
(formerly factory_girl) is the canonical Ruby fixture-factory library - it
builds object graphs with referential integrity that raw faker-data cannot
(factory_bot-readme).
The split:
Faker::Name.name).create(:user, :admin, posts_count: 3)) - including sequence-based unique IDs, associations
between models, and trait composition.User with three Posts and a
Profile, all linked correctly.create(:admin))
rather than per-field overrides.factory_bot_rails (Rails) or factory_bot to the test group and require it.spec/factories/, one attribute block each.trait blocks for variants (:admin, :with_posts) instead of separate variant factories.Faker into attribute blocks for randomized values; use .unique where uniqueness matters.build_stubbed for unit, build for no-DB, create for persistence.FactoryBot::Syntax::Methods in the RSpec / Minitest config to call create(:user) directly.gem install factory_botFor Rails projects, prefer the Rails-specific gem with auto-loading:
# Gemfile
group :test do
gem 'factory_bot_rails' # adds Rails-specific helpers and auto-loads spec/factories/
end(Per factory_bot-readme.)
# spec/factories/users.rb
FactoryBot.define do
factory :user do
name { "John Doe" }
email { "john@example.com" }
end
end(Per factory_bot-readme.)
Each attribute is a block - the block is evaluated lazily at object
creation, so Faker::Name.name re-runs per create call.
For unique values across factory invocations:
sequence :email do |n|
"user#{n}@example.com"
end
factory :user do
email # uses the sequence
end(Per factory_bot-readme.)
factory :post do
title { "Hello" }
body { "World" }
user # implicit reference to a user factory
end
# OR explicit association
factory :post do
user # short form
# association :user, factory: :admin # explicit form
end
# Has-many: build N posts from a user factory
factory :user do
name { "John" }
after(:create) do |user|
create_list(:post, 3, user: user)
end
endTraits compose mix-ins onto a base factory:
factory :user do
name { "John Doe" }
email { "john@example.com" }
trait :admin do
role { "admin" }
end
trait :with_posts do
after(:create) do |user|
create_list(:post, 3, user: user)
end
end
end
# Apply one or more traits at create time
FactoryBot.create(:user, :admin, :with_posts)(Per factory_bot-readme.)
Traits are the canonical way to keep factory bodies DRY - instead of
ten variant factories (:admin_user, :premium_user,
:admin_premium_user, …), one base factory plus N traits composes
to all variants.
Per factory_bot-readme, FactoryBot supports three:
| Strategy | What it does | When to use |
|---|---|---|
build(:x) | Returns an unsaved ActiveRecord object. | Pure-logic tests; no DB persistence needed. |
create(:x) | Saves the object (and any associations) to the database. | Integration tests that exercise persistence. |
build_stubbed(:x) | Returns a stubbed object: appears persisted (new_record? returns false) but never hits the database. | Speed up unit tests that don't actually need DB IO. |
Speed hierarchy: build_stubbed >> build >> create. Use the
weakest one that still tests what you need.
build(:user) # Unsaved User instance
create(:user) # Persisted User (and any associations)
build_stubbed(:user) # Fake-persisted User; `id` is set, `new_record?` is falsePlug Faker into the factory body for randomized values:
require 'faker'
FactoryBot.define do
factory :user do
name { Faker::Name.name }
email { Faker::Internet.unique.email } # `.unique` enforces uniqueness across factory invocations
age { Faker::Number.between(from: 18, to: 80) }
end
endFaker::Internet.unique (and other unique helpers) tracks
generated values per session and raises if exhausted. Use this
instead of a sequence when you want both randomness and
uniqueness.
# spec/spec_helper.rb (or rails_helper.rb for Rails)
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods # enables `create(:user)` instead of `FactoryBot.create(:user)`
end
# spec/users_spec.rb
RSpec.describe User do
it 'has a name' do
user = create(:user)
expect(user.name).to be_present
end
end# test/test_helper.rb
class ActiveSupport::TestCase
include FactoryBot::Syntax::Methods
endPer factory_bot-readme; both syntaxes mirror the same DSL - only the test-framework hookup differs.
A test needs an admin user that owns three posts. Define the factory with two traits and Faker-backed values:
# spec/factories/users.rb
FactoryBot.define do
factory :user do
name { Faker::Name.name }
email { Faker::Internet.unique.email }
trait :admin do
role { "admin" }
end
trait :with_posts do
after(:create) { |user| create_list(:post, 3, user: user) }
end
end
endCompose both traits in one intent-revealing call:
RSpec.describe User do
it 'admin owns three posts' do
user = create(:user, :admin, :with_posts)
expect(user.role).to eq('admin')
expect(user.posts.count).to eq(3)
end
endcreate persists the user and, via the :with_posts after(:create)
hook, its three posts - one line instead of six lines of manual setup.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
FactoryBot.create(:user) in every test, even for read-only assertions | Database overhead per test; suite slows linearly with test count. | Use build_stubbed for unit tests that don't exercise persistence; reserve create for integration tests. |
| One mega-factory with 30 attributes in the base | Every create(:user) writes 30 columns; tests that need 3 attributes pay the cost. | Minimal base factory + traits for the rich variants. |
| Per-test-class factory definitions | Two test files re-define :user differently; subtle bug. | One factory per class, in spec/factories/ (auto-loaded by factory_bot_rails). |
sequence for fields that should be random (e.g. names) | Sequence values are predictable; tests pass on User 1 User 2 patterns that wouldn't pass on real names. | Use Faker for value variety, sequence for uniqueness only. |
create_list(:user, 100) per test | DB write storm; even fast tests stall on bulk insert. | Use build_stubbed_list if persistence isn't required; if it is, use raw SQL bulk insert. |
| Factory associations that always create the parent | A test of Post creates a User; that creates 5 Permissions; etc. - explosion of objects. | Pass an existing parent: create(:post, user: existing_user). |
factory_bot_rails. Pure Ruby projects
must require factory files manually.has_many through:. You can express it
with after(:create) blocks, but the syntax is hand-rolled.user1@example.com ... user5000@example.com. If
asserting on the n-th sequence value, your test breaks when run
in isolation.faker-data - Ruby Faker (the value engine for FactoryBot fields).seed-data-curator - downstream workflow consuming FactoryBot for E2E suite seeds.