# spec/factories/users.rb
FactoryBot.define do
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
name { Faker::Name.name }
password { 'password123' }
trait :admin do
role { 'admin' }
end
trait :with_posts do
transient do
posts_count { 3 }
end
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
trait :inactive do
status { 'inactive' }
deactivated_at { 1.week.ago }
end
factory :admin_user, traits: [:admin]
end
factory :profile do
user
bio { Faker::Lorem.paragraph }
avatar_url { Faker::Avatar.image }
end
end
# spec/factories/posts.rb
FactoryBot.define do
factory :post do
user
title { Faker::Book.title }
content { Faker::Lorem.paragraphs(number: 3).join("\n") }
status { 'draft' }
trait :published do
status { 'published' }
published_at { 1.day.ago }
end
trait :with_comments do
after(:create) do |post|
create_list(:comment, 3, post: post)
end
end
factory :published_post, traits: [:published]
end
end
# Using factories in tests
# Build (not saved)
user = build(:user)
user.persisted? # => false
# Create (saved)
user = create(:user)
user.persisted? # => true
# Build stubbed (fastest - no database)
user = build_stubbed(:user)
user.id # => 1001 (fake ID)
# With traits
admin = create(:user, :admin)
user_with_posts = create(:user, :with_posts, posts_count: 5)
# Override attributes
user = create(:user, email: 'specific@example.com', name: 'John')
# Associations
post = create(:post) # Automatically creates associated user
post.user.present? # => true
# Explicit association
user = create(:user)
post = create(:post, user: user)
# Create list
users = create_list(:user, 5)
admins = create_list(:user, 3, :admin)
# Attributes for (hash without create)
attrs = attributes_for(:user)
# => { email: "...", name: "...", password: "..." }
Factory Bot creates test data with minimal boilerplate. Factories define blueprints for model creation. I use traits for variations—published posts, admin users. Sequences generate unique values. Associations automatically create related records. Transient attributes pass data without persisting. Callbacks modify objects after build/create. build creates unsaved instances; create persists to database. build_stubbed creates objects bypassing database entirely—fastest for unit tests. Factories keep tests maintainable—updating schemas requires only factory changes. I prefer explicit associations over create_list for clarity. FactoryBot makes TDD enjoyable by eliminating test data drudgery.