spec-performance
This skill should be used when the user asks about "slow specs", "test performance", "parallel tests", "spec profiling", "let vs let!", "build vs create", "test optimization", or needs guidance on making RSpec tests faster.
What this skill does
# Spec Performance
Slow tests reduce productivity and discourage running tests frequently. This skill covers techniques for optimizing RSpec test suite performance.
## Profiling Slow Tests
### Built-in Profiler
```bash
# Show 10 slowest examples
rspec --profile 10
# In spec_helper.rb for always-on profiling
RSpec.configure do |config|
config.profile_examples = 10
end
```
### Detailed Timing
```ruby
# spec/support/timing.rb
RSpec.configure do |config|
config.around(:each) do |example|
start = Time.now
example.run
elapsed = Time.now - start
puts "#{example.full_description}: #{elapsed.round(2)}s" if elapsed > 1
end
end
```
## Database Optimization
### Prefer build Over create
```ruby
# Slow - hits database
let(:user) { create(:user) }
# Fast - in memory only
let(:user) { build(:user) }
# Fastest - stubbed, no DB
let(:user) { build_stubbed(:user) }
```
### When to Use Each Strategy
| Strategy | Use When |
|----------|----------|
| `build` | Testing validations, object behavior |
| `build_stubbed` | Need ID, testing presentation |
| `create` | Testing DB queries, associations, callbacks |
### Minimize Database Writes
```ruby
# Slow - creates 3 users
it "lists users" do
create(:user)
create(:user)
create(:user)
expect(User.count).to eq(3)
end
# Better - single batch insert
it "lists users" do
User.insert_all([
{ email: "[email protected]" },
{ email: "[email protected]" },
{ email: "[email protected]" }
])
expect(User.count).to eq(3)
end
```
### Use before(:all) Carefully
```ruby
# Creates user once for all examples
before(:all) do
@user = create(:user)
end
after(:all) do
@user.destroy
end
# Warning: shared state between examples
# Use only for read-only data
```
## let vs let!
### let - Lazy Evaluation
```ruby
let(:user) { create(:user) }
it "does something" do
# User created here, when first accessed
user.name
end
it "does something else" do
# User not created if not referenced
expect(true).to be true
end
```
### let! - Eager Evaluation
```ruby
let!(:user) { create(:user) }
it "has a user in database" do
# User already created before this runs
expect(User.count).to eq(1)
end
```
### When to Use let!
```ruby
# Use let! when:
# 1. Callback side effects needed
let!(:user) { create(:user) } # Triggers after_create callbacks
# 2. Database state must exist before test
let!(:existing_user) { create(:user, email: "[email protected]") }
it "validates uniqueness" do
new_user = build(:user, email: "[email protected]")
expect(new_user).not_to be_valid
end
```
### Avoid Unnecessary let!
```ruby
# Bad - always creates even when not needed
let!(:user) { create(:user) }
let!(:post) { create(:post, user: user) }
let!(:comment) { create(:comment, post: post) }
# Good - only create what's needed per test
let(:user) { create(:user) }
context "with posts" do
let(:post) { create(:post, user: user) }
it "lists posts" do
post # Triggers creation
expect(user.posts).to include(post)
end
end
```
## Parallel Testing
### parallel_tests Gem
```ruby
# Gemfile
gem "parallel_tests", group: [:development, :test]
# Setup
rake parallel:setup
rake parallel:create
rake parallel:migrate
# Run tests
rake parallel:spec
# or
parallel_rspec spec/
```
### Database Configuration
```yaml
# config/database.yml
test:
database: myapp_test<%= ENV['TEST_ENV_NUMBER'] %>
```
### Avoiding Parallelization Issues
```ruby
# Use unique data per process
let(:email) { "user#{Process.pid}@test.com" }
# Avoid shared files
let(:file_path) { Rails.root.join("tmp/test_#{Process.pid}.txt") }
```
## Mocking and Stubbing for Speed
### Stub External Services
```ruby
# Slow - real HTTP call
it "fetches data" do
result = ExternalApi.fetch(id: 1)
expect(result).to be_present
end
# Fast - stubbed response
it "fetches data" do
allow(ExternalApi).to receive(:fetch).and_return({ data: "test" })
result = ExternalApi.fetch(id: 1)
expect(result).to eq({ data: "test" })
end
```
### Use WebMock for HTTP
```ruby
# Gemfile
gem "webmock", group: :test
# spec/spec_helper.rb
require "webmock/rspec"
WebMock.disable_net_connect!(allow_localhost: true)
# In specs
stub_request(:get, "https://api.example.com/users")
.to_return(status: 200, body: { users: [] }.to_json)
```
### Stub Time-Consuming Methods
```ruby
# Slow - actual file processing
it "processes file" do
result = FileProcessor.process(large_file)
expect(result).to be_present
end
# Fast - stub the slow method
it "processes file" do
allow(FileProcessor).to receive(:process).and_return({ success: true })
result = FileProcessor.process(large_file)
expect(result).to eq({ success: true })
end
```
## Test Data Optimization
### Use build_stubbed for Speed
```ruby
# build_stubbed creates objects with:
# - Fake IDs (but not nil)
# - Fake timestamps
# - No database writes
user = build_stubbed(:user)
user.id # => 1001 (fake but present)
user.persisted? # => true (pretends to be saved)
```
### Minimal Factory Attributes
```ruby
# Slow - lots of associations
factory :order do
user
shipping_address
billing_address
coupon
association :items, count: 5
end
# Fast - minimal required attributes
factory :order do
status { "pending" }
total { 100 }
trait :with_user do
user
end
end
```
### Avoid N+1 in Test Setup
```ruby
# Slow - N+1 queries in setup
let(:users) { create_list(:user, 10) }
before do
users.each { |u| create(:post, user: u) }
end
# Better - batch operations
before do
users = create_list(:user, 10)
Post.insert_all(users.map { |u| { user_id: u.id, title: "Post" } })
end
```
## CI Optimization
### Fail Fast
```ruby
# spec/spec_helper.rb
RSpec.configure do |config|
config.fail_fast = ENV["CI"].present?
end
```
### Bisect for Flaky Tests
```bash
# Find minimal set that reproduces failure
rspec --bisect
```
### Example Status Persistence
```ruby
# spec/spec_helper.rb
RSpec.configure do |config|
# Re-run only failed specs first
config.example_status_persistence_file_path = "spec/examples.txt"
end
```
## Performance Checklist
### Quick Wins
- [ ] Use `build` instead of `create` when possible
- [ ] Use `build_stubbed` for presentation tests
- [ ] Stub external HTTP calls
- [ ] Avoid `let!` when `let` works
### Medium Effort
- [ ] Set up parallel testing
- [ ] Profile and fix slowest specs
- [ ] Minimize factory associations
- [ ] Use `before(:all)` for read-only setup
### Advanced
- [ ] Database cleaner strategy optimization
- [ ] Shared database connections for system specs
- [ ] Spring preloader for development
- [ ] CI caching for gems and assets
## Benchmarking Helpers
```ruby
# spec/support/benchmark_helper.rb
module BenchmarkHelper
def measure(label = "Block", &block)
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
result = block.call
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
puts "#{label}: #{(elapsed * 1000).round(2)}ms"
result
end
end
# Usage in specs
include BenchmarkHelper
it "performs quickly" do
measure("User creation") { create(:user) }
measure("User build") { build(:user) }
end
```
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.