Skip to content

What it is

FactoryBot builds test objects from declarative factories, replacing static fixtures with composable, overridable definitions.

Define factories with defaults, then build or create instances, overriding only what a test cares about. Traits and associations compose.

Installation

gem 'factory_bot_rails', group: %i[development test]

Getting started

The smallest useful thing you can do with it, and what each part means.

Factories, traits and sequences
FactoryBot.define do
  factory :book do
    sequence(:title) { |n| "Book #{n}" }   # unique per instance
    year { 1990 }
    association :author

    trait :classic do
      year { 1950 }
    end

    trait :with_reviews do
      # after(:create) because reviews need a persisted book.
      after(:create) { |book| create_list(:review, 3, book: book) }
    end

    factory :classic_book, traits: [:classic]
  end
end

build(:book)                          # in memory, not saved
create(:book, title: 'Dune')          # persisted
create(:book, :classic, :with_reviews)
create_list(:book, 5, year: 2020)
Stating only the attribute under test is the point: a test that says `create(:book, year: 1850)` makes it obvious that the year is what matters and everything else is incidental.

Advanced usage

Where the library earns its place over a simpler alternative.

Keeping factories fast and valid
# build_stubbed does not touch the database at all — much faster
# when persistence is irrelevant to the test.
let(:book) { build_stubbed(:book) }

# Lint every factory in CI so a schema change cannot silently break them.
RSpec.describe 'factories' do
  it 'are all valid' do
    expect { FactoryBot.lint(traits: true) }.not_to raise_error
  end
end

# Avoid creating associations that the test does not need.
factory :book do
  association :author, strategy: :build   # not persisted unless required
end
FactoryBot.lint in CI is the safeguard that matters: without it, a schema change can leave dozens of factories invalid and every failure looks unrelated to the cause.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Validation failed: title has already been taken
A static default on a unique column. Use a sequence.
Tests are slow
Factories are creating deep association trees. Use build_stubbed, or strategy: :build on associations.

Best practices

  • Use build_stubbed when the test does not need the database — it is dramatically faster.
  • Keep factories minimal and valid; add extras through traits rather than defaults.
  • Run FactoryBot.lint in CI so schema drift is caught immediately.
  • Use sequences for unique columns to avoid uniqueness collisions between examples.

Background

Why it exists, and what it was reacting to.

Fixtures are global, order-dependent and drift from the schema. FactoryBot lets each test build exactly the object it needs, stating only the attributes relevant to that test.