factory-bot
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.
What this skill does
# Factory Bot
Factory Bot provides a framework for setting up Ruby objects as test data. It replaces fixtures with a more flexible and maintainable approach.
## Setup
```ruby
# Gemfile
group :development, :test do
gem "factory_bot_rails"
end
# spec/rails_helper.rb
RSpec.configure do |config|
config.include FactoryBot::Syntax::Methods
end
```
## Basic Factories
### Defining Factories
```ruby
# spec/factories/users.rb
FactoryBot.define do
factory :user do
first_name { "John" }
last_name { "Doe" }
email { "[email protected]" }
password { "password123" }
admin { false }
end
end
```
### Using Factories
```ruby
# 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)
```
## Sequences
Generate unique values:
```ruby
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
sequence(:username) { |n| "user_#{n}" }
end
# Creates: [email protected], [email protected], 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
```
## Dynamic Attributes
Use blocks for computed values:
```ruby
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
```
## Associations
### belongs_to
```ruby
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
```
### has_many
```ruby
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
```
### has_many with trait
```ruby
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)
```
## Traits
Define reusable attribute sets:
```ruby
factory :user do
first_name { "John" }
email { "[email protected]" }
admin { false }
trait :admin do
admin { true }
email { "[email protected]" }
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)
```
### Combining Traits
```ruby
# 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]
```
## Transient Attributes
Pass data to factory without setting attributes:
```ruby
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)
```
## Callbacks
Execute code at different lifecycle points:
```ruby
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
```
## Inheritance
```ruby
factory :user do
first_name { "John" }
email { "[email protected]" }
factory :admin do
admin { true }
email { "[email protected]" }
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
```
## Build Strategies
| 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 |
```ruby
# Performance comparison
build(:user) # Fast, no database
build_stubbed(:user) # Fastest, fake persistence
create(:user) # Slowest, hits database
```
### When to Use Each
```ruby
# 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) }
```
## Factory Lists
```ruby
# 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
```
## Linting Factories
Validate all factories are correctly defined:
```ruby
# 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
```
## Best Practices
### Minimal Factories
Define only required attributes:
```ruby
# 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
```
### Use Traits for Variations
```ruby
# 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
```
### Avoid Building What You Don't Need
```ruby
# 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) }
```
Related in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.