This skill should be used when the user asks about "factories", "FactoryBot", "factory_bot", "traits", "sequences", "build vs create", "transient attributes", "associations in factories", or needs guidance on test data generation in RSpec.
Provides guidance on creating and using Factory Bot for test data in RSpec.
npx claudepluginhub bastos/ruby-plugin-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Factory Bot provides a framework for setting up Ruby objects as test data. It replaces fixtures with a more flexible and maintainable approach.
# Gemfile
group :development, :test do
gem "factory_bot_rails"
end
# spec/rails_helper.rb
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
# spec/factories/users.rb
FactoryBot.define do
factory :user do
first_name { "John" }
last_name { "Doe" }
email { "john@example.com" }
password { "password123" }
admin { false }
end
end
# Create and save to database
user = create(:user)
# Build without saving (in-memory)
user = build(:user)
# Create with attribute overrides
user = create(:user, first_name: "Jane", admin: true)
# Build stubbed (fastest, no database)
user = build_stubbed(:user)
# Get attributes as hash
attrs = attributes_for(:user)
Generate unique values:
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
sequence(:username) { |n| "user_#{n}" }
end
# Creates: user1@example.com, user2@example.com, etc.
# Named sequences (reusable)
sequence :email do |n|
"person#{n}@example.com"
end
factory :user do
email { generate(:email) }
end
factory :admin do
email { generate(:email) }
end
Use blocks for computed values:
factory :user do
first_name { "John" }
last_name { "Doe" }
email { "#{first_name.downcase}.#{last_name.downcase}@example.com" }
created_at { Time.current }
birth_date { 25.years.ago }
end
# Faker for realistic data
factory :user do
first_name { Faker::Name.first_name }
last_name { Faker::Name.last_name }
email { Faker::Internet.email }
phone { Faker::PhoneNumber.phone_number }
bio { Faker::Lorem.paragraph }
end
factory :post do
title { "My Post" }
body { "Content" }
user # Automatically creates associated user
end
# Override association
post = create(:post, user: specific_user)
# Explicit association
factory :post do
association :user
# or with factory name different from attribute
association :author, factory: :user
end
factory :user do
first_name { "John" }
# Using transient + callback
transient do
posts_count { 0 }
end
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
end
# Usage
user = create(:user, posts_count: 3)
user.posts.count # => 3
factory :user do
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
end
user = create(:user, :with_posts)
user = create(:user, :with_posts, posts_count: 5)
Define reusable attribute sets:
factory :user do
first_name { "John" }
email { "john@example.com" }
admin { false }
trait :admin do
admin { true }
email { "admin@example.com" }
end
trait :with_avatar do
after(:create) do |user|
user.avatar.attach(
io: File.open(Rails.root.join("spec/fixtures/avatar.png")),
filename: "avatar.png"
)
end
end
trait :inactive do
active { false }
deactivated_at { 1.day.ago }
end
end
# Usage
admin = create(:user, :admin)
inactive_admin = create(:user, :admin, :inactive)
user_with_avatar = create(:user, :with_avatar)
# Multiple traits
create(:user, :admin, :with_avatar, :inactive)
# Traits with overrides
create(:user, :admin, first_name: "Super Admin")
# Factory inheriting traits
factory :admin, traits: [:admin]
factory :inactive_user, traits: [:inactive]
Pass data to factory without setting attributes:
factory :user do
transient do
upcased { false }
skip_confirmation { false }
end
first_name { "John" }
after(:create) do |user, evaluator|
user.update!(first_name: user.first_name.upcase) if evaluator.upcased
user.confirm! if evaluator.skip_confirmation
end
end
create(:user, upcased: true)
create(:user, skip_confirmation: true)
Execute code at different lifecycle points:
factory :user do
after(:build) do |user|
# After building, before save
end
before(:create) do |user|
# Just before save
end
after(:create) do |user|
# After save
end
after(:stub) do |user|
# After build_stubbed
end
end
# Callback with evaluator (access transient attributes)
after(:create) do |user, evaluator|
create_list(:post, evaluator.posts_count, user: user)
end
factory :user do
first_name { "John" }
email { "john@example.com" }
factory :admin do
admin { true }
email { "admin@example.com" }
end
factory :super_admin, parent: :admin do
super_admin { true }
end
end
create(:user) # Regular user
create(:admin) # Admin user
create(:super_admin) # Super admin
| Strategy | Database | ID | Best For |
|---|---|---|---|
create | Yes | Real | Integration tests |
build | No | nil | Unit tests, validations |
build_stubbed | No | Fake | Fastest, isolated tests |
attributes_for | No | - | Controller params |
# Performance comparison
build(:user) # Fast, no database
build_stubbed(:user) # Fastest, fake persistence
create(:user) # Slowest, hits database
# build - testing object behavior without persistence
user = build(:user)
expect(user).to be_valid
# build_stubbed - mocking persisted objects
user = build_stubbed(:user)
expect(user.id).to be_present # Has fake ID
# create - testing database interactions
user = create(:user)
expect(User.find(user.id)).to eq(user)
# attributes_for - controller params
post users_path, params: { user: attributes_for(:user) }
# Create multiple records
users = create_list(:user, 3)
users = create_list(:user, 3, admin: true)
# Build multiple (no save)
users = build_list(:user, 5)
# With different attributes
users = create_list(:user, 3) do |user, i|
user.update!(position: i)
end
Validate all factories are correctly defined:
# spec/factories_spec.rb
RSpec.describe "Factories" do
it "has valid factories" do
FactoryBot.lint
end
# Faster: only lint traits
it "has valid factories" do
FactoryBot.lint(traits: true)
end
end
Define only required attributes:
# Good - minimal
factory :user do
email { generate(:email) }
password { "password" }
end
# Avoid - too much default data
factory :user do
email { generate(:email) }
password { "password" }
first_name { "John" }
last_name { "Doe" }
phone { "555-1234" }
address { "123 Main St" }
# ... lots more
end
# Good - traits for specific states
factory :order do
trait :pending do
status { "pending" }
end
trait :shipped do
status { "shipped" }
shipped_at { 1.day.ago }
end
end
# Avoid - many separate factories
factory :pending_order
factory :shipped_order
factory :cancelled_order
# Good - build associations only when needed
let(:user) { create(:user) }
let(:post) { create(:post, user: user) }
# Avoid - creating unused associated records
let(:user) { create(:user, :with_posts, :with_comments) }
references/factory-patterns.md - Advanced factory patternsreferences/faker-examples.md - Faker gem examplesexamples/factories/users.rb - Complete user factoryexamples/factories/orders.rb - Complex association example