Testland
Browse all skills & agents

factory-bot-data

Authors Ruby FactoryBot factories with traits, associations, sequences, and the three build strategies (build / create / build_stubbed); integrates with RSpec / Minitest test suites; pairs with Faker for randomized field values. Use when the project is Ruby / Rails and needs structured fixture creation with referential integrity.

Install with skills.sh (any agent)

npx skills add testland/qa --skill factory-bot-data
View source

factory-bot-data

Overview

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 (opens in new window)).

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 (opens in new window).)

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 (opens in new window).)

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 (opens in new window).)

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 (opens in new window).)

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

FactoryBot supports three: build (unsaved), create (persisted with associations), and build_stubbed (fake-persisted, never hits the DB). Speed hierarchy: build_stubbed >> build >> create - use the weakest one that still tests what you need. Full table and examples: references/strategies-and-anti-patterns.md.

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 (opens in new window); 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

Common FactoryBot pitfalls - create for read-only tests, mega base factories, per-test-class definitions, sequence where values should be random, bulk create_list, and always-create associations - and their fixes are catalogued in references/strategies-and-anti-patterns.md.

Limitations

  • Ruby-only. For other languages: factory_boy (Python), fishery (TS), Bogus (.NET), 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 (opens in new window) - canonical: install, factory definition, traits, associations, sequences, build / create / build_stubbed strategies.
  • faker-data - Ruby Faker (the value engine for FactoryBot fields).
  • synthetic-data-tool-selector - dispatcher selecting the right factory library per language.
  • seed-data-curator - downstream workflow consuming FactoryBot for E2E suite seeds.

FactoryBot build strategies and anti-patterns

View source (opens in new window)

FactoryBot build strategies and anti-patterns

Reference detail for factory-bot-data (opens in new window): the three build strategies and the factory anti-pattern catalog.

Build strategies

Per factory_bot-readme (opens in new window), 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

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

Related skills

bogus-data

Authors .NET test fixtures using the Bogus library - fluent typed `Faker` builders with `.RuleFor` per property, generation via `Generate()` / `GenerateBetween(min, max)` / `GenerateLazy()`, and `UseSeed()` for reproducibility. Provides the Bogus equivalent of Python's Faker / Ruby's FactoryBot. Use when the project is C# / F# / VB.NET and the team needs typed fixture creation.

boundary-value-generator

Generates boundary-value test cases from typed input specifications - for each input field, produces the canonical 6-point set (one below, at, and above the lower bound; one below, at, and above the upper bound) plus equivalence-class representatives. Emits cases as parameterized test inputs (pytest @parametrize / Jest test.each / xUnit InlineData / etc.). Use when a function or endpoint has numeric / string-length / collection-size constraints and the team needs systematic edge-case coverage.

e2e-test-narrative-builder

Assembles a multi-step end-to-end user-journey test from a list of high-level user intents - translates each intent ("user signs up", "user adds product to cart", "user completes checkout with promo code") into the corresponding test-runner step (Playwright / Cypress / Selenium / Karate), wires shared state across steps via test fixtures, and emits the resulting test as a single Scenario in the project's E2E framework. Use when scaffolding an E2E test that exercises a complete user flow rather than a single page.

faker-data

Authors test-data factories using Faker: the Python `faker` library, the `@faker-js/faker` JS port, and the `faker-ruby` gem. Owns the library mechanics end to end: install per language, the provider catalogue (person / internet / location / date / finance / lorem), locale selection and multi-locale mode, and seed-based determinism for reproducible runs. Scope is generating fresh values for tests that start from nothing, not replacing values inside an existing dataset that already holds real records, which raises referential-integrity and re-identification concerns this skill does not address. Prefer this skill when the codebase already uses the Faker family or when cross-language consistency across Python, JS, and Ruby matters; use mimesis-data only when deeper Python locale coverage is the primary requirement. Use when authoring fixtures or factories that need realistic-looking field values.

golden-file-conventions

Reference catalog for snapshot / golden file management - naming conventions, directory layout, when to add / update / remove a baseline, sanitization (timestamps, IDs, PII), per-OS / per-runtime variant strategy, and review workflow for snapshot diffs in PRs. Use when designing a snapshot-testing convention or auditing an existing one for drift.

malicious-payload-bank

Reference catalog of curated adversarial input payloads keyed by attack class - SQL injection, XSS, SSRF, path traversal, command injection, XXE, prototype pollution, regex DoS, Unicode confusables, header injection - plus per-context guidance for which payloads apply (URL parameter / form input / JSON body / file upload). Use when authoring negative-test cases for input validation, fuzz targets, or a security-focused test suite that needs to exercise the OWASP Top 10 attack surface.

mimesis-data

Authors Python test fixtures using mimesis - a fast, type-hinted, locale-aware test-data generator with 46 locales - covering Person / Address / Internet / Datetime providers and the Schema/Field pattern for typed-dict generation. Pairs with factory_boy when referential integrity is needed. Use when the project is Python and the team values speed, type hints, or strong locale coverage over Faker's larger ecosystem.

mountebank-imposters

Authors Mountebank imposters (multi-protocol mock servers - HTTP, HTTPS, TCP, SMTP, LDAP, gRPC, WebSockets, GraphQL, and more) by POSTing JSON definitions to the Mountebank control API on port 2525, configures stubs with predicates and responses, and uses record-playback proxy mode to capture upstream traffic. Use when the project needs a multi-protocol mock server beyond HTTP-only tools like WireMock or MSW.

msw-handlers

Authors Mock Service Worker (MSW) request handlers for both browser and Node.js test environments using the `http.get` / `http.post` / `HttpResponse.json` API, wires them via `setupWorker` (browser) or `setupServer` (Node), and manages the test lifecycle (`server.listen` / `resetHandlers` / `close`). Use when the project uses JavaScript / TypeScript and needs to mock fetch / XHR at the network layer for both Vitest / Jest unit tests and Cypress / Playwright integration tests.

negative-test-generator

Generates negative / error-path test cases that mirror happy-path tests - for each happy-path test, produces companions exercising input validation rejection, missing required fields, type mismatches, authorization failures, rate-limit errors, and adversarial payloads from the malicious-payload-bank. Emits cases as parameterized tests in the project's runner format. Use when a feature has happy-path coverage but the rejection / error / unauthorized paths are untested.

pairwise-test-case-generator

Generates parameterized test inputs combining boundary-value, equivalence-class, and pairwise-combinatorial cases from a typed multi-input specification - produces the cross-product of cases up to a configurable strength (1-wise / 2-wise / N-wise) using all-pairs reduction so the test surface stays tractable. Emits cases in the project's test-runner-native parametrize format. Use when a function or endpoint takes 3+ inputs whose interactions matter and full Cartesian product would explode.

seed-data-curator

Builds a reproducible E2E seed dataset for the project's test environments - picks a representative user / org / data-product cross-section, generates the rows via the project's chosen factory library (FactoryBot / mimesis / Bogus / Faker + factory_boy), persists the dataset as a checked-in fixture (SQL dump / JSON / per-engine seed file), and wires it into the test bootstrap. Use when starting E2E coverage on a project that has no seed strategy, or when an existing seed has drifted.

synthetic-data-tool-selector

Chooses between the four mainstream synthetic test-data generators - Faker (JavaScript), FactoryBot (Ruby), mimesis (Python), Bogus (.NET) - picks the right tool by language and use case (raw value generation vs. typed factory orchestration), shows side-by-side equivalents for the same fixture across all four, and emits the language-appropriate code. Use when starting test-data work on a project and the team wants the "which tool should I use" decision documented.

synthetic-pii-generator

Generates realistic-but-fake personally identifiable information (PII) - emails, phone numbers, SSNs / national IDs, addresses, names, credit-card numbers (test BIN ranges), date-of-birth - for non-production environments. Wraps Faker / mimesis with PII-aware constraints so generated values match real format expectations (Luhn-valid card numbers, region-valid phone formats, ITIN/SSN format) without ever generating real-person data. Use when seeding test environments, building demo data, or replacing real PII in copied datasets.

test-data-patterns

Pure reference catalog of the cross-language object-construction patterns for test data - Test Data Builder (Pryce/Freeman), Factory (with traits and associations), Object Mother, Fixture composition (per-test / per-describe / shared), Snapshot (defers to `golden-file-conventions` for the operational details), and Production-Data Anonymisation. Distinct from per-language data wrappers (`factory-bot-data` Ruby, `faker-data` JS, `mimesis-data` Python, `bogus-data` .NET) which document tool-specific configuration; this catalog is the architecture-tier reference for choosing **which pattern** before reaching for the tool. Use when choosing a test-data construction pattern for a new suite, or auditing an existing suite whose fixtures have drifted into shared mutable state.

wiremock-stubs

Authors WireMock stub mappings for HTTP service mocking - `stubFor` with verb/path/header matchers + `willReturn` response shaping, lifecycle via `WireMockServer` (start / stop) or JUnit `WireMockExtension`, request verification via `verify()`, and dynamic-port allocation for parallel tests. Use when the project is JVM-based and tests need to mock HTTP dependencies (third-party APIs, internal microservices) at the network layer.