Skip to content

Factory Bot — Test Data Factories in Rails

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Factory Bot. We cover key concepts, practical examples, and best practices to help you master this topic.

Factory Bot creates test data with factories, traits, sequences, associations, and callbacks, supporting build, create, and attributes_for strategies for different testing needs.

What You'll Learn

By the end of this tutorial, you'll define factories with traits and sequences, handle associations, use transient attributes, implement callbacks, and choose the right creation Strategy.

Why Factory Bot Matters

Factory Bot replaces fixtures with flexible, maintainable test data definitions. Traits modify data for specific scenarios without duplicating factory definitions.

Real-World Use

A blog test suite uses a Post factory with traits for published, drafted, and featured states. A User factory generates unique emails via sequences and creates associated profiles.

Factory Path

flowchart LR
  A[RSpec Testing] --> B[Factory Bot]
  B --> C[Factories]
  B --> D[Traits]
  B --> E[Associations]
  B --> F[Sequences]
  B --> G{You Are Here}
  style G fill:#f90,color:#fff

Defining Factories

Create test data factories.

# spec/factories/users.rb
FactoryBot.define do
  factory :user do
    name { "John Doe" }
    email { generate(:email) }
    password { "password123" }
    password_confirmation { "password123" }
    confirmed_at { Time.current }

    trait :admin do
      role { :admin }
    end

    trait :unconfirmed do
      confirmed_at { nil }
    end
  end
end

Sequences

Generate unique values.

FactoryBot.define do
  sequence(:email) { |n| "user#{n}@example.com" }
  sequence(:sku) { |n| "SKU-#{n.to_s.rjust(5, "0")}" }
  sequence(:slug) { |n| "post-slug-#{n}" }

  factory :product do
    name { "Product #{generate(:sku)}" }
    sku
    price { 9.99 }
    association :category

    trait :expensive do
      price { 999.99 }
    end
  end
end

Associations

Define model associations in factories.

FactoryBot.define do
  factory :post do
    title { "My Post" }
    body { "Post body content" }
    association :author, factory: :user

    trait :with_comments do
      after(:create) do |post|
        create_list(:comment, 3, post: post)
      end
    end

    trait :with_tags do
      after(:create) do |post|
        post.tags << create_list(:tag, 2)
      end
    end
  end

  factory :comment do
    body { "Great post!" }
    association :post
    association :author, factory: :user
  end
end

Transient Attributes

Use transient attributes for conditional setup.

FactoryBot.define do
  factory :order do
    transient do
      item_count { 1 }
      total { nil }
    end

    status { :pending }
    association :user

    after(:create) do |order, evaluator|
      create_list(:order_item, evaluator.item_count, order: order)
      if evaluator.total
        order.update_column(:total, evaluator.total)
      else
        order.update_column(:total, order.items.sum(&:total))
      end
    end

    trait :with_items do
      item_count { 3 }
    end
  end
end

# Usage
create(:order)                    # 1 item
create(:order, item_count: 5)     # 5 items
create(:order, total: 100.0)      # Fixed total

Callbacks

Run code during factory lifecycle.

FactoryBot.define do
  factory :invoice do
    association :order

    after(:build) do |invoice|
      invoice.invoice_number ||= "INV-#{Time.current.to_i}"
    end

    after(:create) do |invoice|
      InvoiceMailer.send_invoice(invoice).deliver_later
    end

    after(:stub) do |invoice|
      invoice.id ||= 123
    end
  end
end

Common Mistakes

1. Slow Factory Creation

Creating deeply nested associations in factories makes tests slow. Use build_stubbed for unit tests.

2. Overusing Traits

Too many traits make factories hard to read. Combine traits with transient attributes.

3. Not Using build_stubbed

build_stubbed creates in-memory objects without database persistence. Use it for non-query tests.

4. Circular Factory Dependencies

Factory A creates Factory B which creates Factory A. Use association without factory creation.

5. Hardcoded Values That Cause Uniqueness Violations

Always use sequences for unique fields like email or slug.

Practice Questions

1. What is the difference between build and create?

build creates an in-memory object. create persists to the database.

2. What is a trait in Factory Bot?

A named variation of a factory that overrides or adds attributes.

3. How do you create a list of records?

Use create_list(:product, 5) to create 5 products.

4. What is build_stubbed for?

Creates a fake record that acts like a persisted record but is not saved.

5. Challenge: Create a factory with associations and traits for a blog.

FactoryBot.define do
  factory :article do
    title { "Article Title" }
    body { "Article body" }
    association :author, factory: :user

    trait :published do
      published_at { 1.hour.ago }
    end

    trait :draft do
      published_at { nil }
    end

    trait :with_comments do
      after(:create) { |a| create_list(:comment, 3, article: a) }
    end
  end
end

FAQ

Should I use fixtures or factories?

Factories are more flexible. Fixtures are faster but harder to maintain.

What is attributes_for?

Returns a hash of attributes without creating an object.

Can I reuse factories across projects?

Yes. Extract shared factories to a gem or use factory inheritance.

How do I create unique test data?

Use sequences for any field that must be unique.

What is the best strategy for unit tests?

build_stubbed for most unit tests. create for integration tests.

Mini Project: Complete Factory Suite

Create factories for an e-commerce application.

FactoryBot.define do
  factory :product do
    name { "Widget" }
    price { 19.99 }
    association :category

    trait :on_sale do
      sale_price { 14.99 }
    end

    trait :out_of_stock do
      quantity { 0 }
    end
  end

  factory :order do
    association :user
    transient do
      products_count { 1 }
    end
    after(:create) do |order, eval|
      create_list(:order_item, eval.products_count, order: order)
    end
  end
end

What's Next

Rails Faker Rails Shoulda Matchers Rails System Tests

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro