CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/synthetic-data-toolkit

Umbrella for the synthetic test data generators beyond plain Faker - FactoryBot (Ruby factories with traits, associations, and build / create / build_stubbed strategies), Mimesis (fast type-hinted Python generator with the Schema/Field bulk pattern and 46 locales), and Bogus (.NET typed `Faker<T>` builders with `.RuleFor` / `StrictMode` / `UseSeed`). Picks the right generator by language and job, shows side-by-side equivalents of the same fixture across all four ecosystems, and carries each tool's full workflow in references/ (factory-bot.md, mimesis.md, bogus.md). faker-data stays the default for plain field values in Python / JS / Ruby; use this skill when the project needs typed factory orchestration, .NET fixtures, or a documented "which tool should I use" decision.

72

Quality

91%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

factory-bot.mdreferences/

FactoryBot (Ruby) - full workflow

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 generates field values (Faker::Name.name).
  • FactoryBot orchestrates object creation (create(:user, :admin, posts_count: 3)) - including sequence-based unique IDs, associations between models, and trait composition.

When to use

  • The project is Ruby (Rails or pure Ruby).
  • Tests need structured fixtures: a User with three Posts and a Profile, all linked correctly.
  • The test suite uses RSpec or Minitest - both are first-class.
  • The team wants "intent-revealing" fixture names (create(:admin)) rather than per-field overrides.

How to use

  1. Add factory_bot_rails (Rails) or factory_bot to the test group and require it.
  2. Define one base factory per model in spec/factories/, one attribute block each.
  3. Add trait blocks for variants (:admin, :with_posts) instead of separate variant factories.
  4. Wire Faker into attribute blocks for randomized values; use .unique where uniqueness matters.
  5. Pick a build strategy per test: build_stubbed for unit, build for no-DB, create for persistence.
  6. Include FactoryBot::Syntax::Methods in the RSpec / Minitest config to call create(:user) directly.
  7. Call the factory in the test and assert on the returned object.

Install

gem install factory_bot

For 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.)

Authoring

Basic factory

# 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.

Sequences

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.)

Associations

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
end

Traits

Traits 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.

Build strategies

Per factory_bot-readme, FactoryBot supports three:

StrategyWhat it doesWhen 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 false

Integrating with Faker

Plug 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
end

Faker::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.

Test framework integration

RSpec

# 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

Minitest

# test/test_helper.rb
class ActiveSupport::TestCase
  include FactoryBot::Syntax::Methods
end

Per factory_bot-readme; both syntaxes mirror the same DSL - only the test-framework hookup differs.

Worked example

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
end

Compose 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
end

create persists the user and, via the :with_posts after(:create) hook, its three posts - one line instead of six lines of manual setup.

Anti-patterns

Anti-patternWhy it failsFix
FactoryBot.create(:user) in every test, even for read-only assertionsDatabase 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 baseEvery 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 definitionsTwo 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 testDB 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 parentA test of Post creates a User; that creates 5 Permissions; etc. - explosion of objects.Pass an existing parent: create(:post, user: existing_user).

Limitations

  • Ruby-only. For other languages: factory_boy (Python), fishery (TS), Bogus (.NET - see bogus.md), MockK (Kotlin).
  • Auto-load only with factory_bot_rails. Pure Ruby projects must require factory files manually.
  • No native support for has_many through:. You can express it with after(:create) blocks, but the syntax is hand-rolled.
  • Sequence resets on test reload. A long-running suite generates user1@example.com ... user5000@example.com. If asserting on the n-th sequence value, your test breaks when run in isolation.

References

  • factory_bot-readme - canonical: install, factory definition, traits, associations, sequences, build / create / build_stubbed strategies.
  • faker-data - Ruby Faker (the value engine for FactoryBot fields).
  • seed-data-curator - downstream workflow consuming FactoryBot for E2E suite seeds.

SKILL.md

tile.json