Rails Testing with RSpec
RSpec setup, factories, system specs, request specs, and fast suites.
# CLAUDE.md — Rails Testing with RSpec
## Setup
- **rspec-rails** + **factory_bot_rails** + **faker** + **shoulda-matchers** + **capybara** (for system specs).
- Run with `bin/rspec`. Parallel via `parallel_tests` if your suite is slow enough to matter.
- One spec per file under test: `spec/models/user_spec.rb` mirrors `app/models/user.rb`.
## Factories over fixtures
- Factories live in `spec/factories/`. One file per model.
- Use `Faker` for realistic strings — but seeds the same way every run for stability (`Faker::Config.random = Random.new(42)` in `rails_helper`).
- Traits for variations: `factory :user do; trait(:admin) { role :admin } end`.
## Spec types
- **Model specs** — validations, scopes, methods. Fast.
- **Request specs** — full HTTP round-trip. Replaces controller specs.
- **System specs** — Capybara + headless Chrome for end-to-end flows.
- Skip controller specs and view specs in new projects. Request and system specs cover their ground.
## Database
- `transactional_fixtures = true` in most cases — each example runs in a transaction that rolls back.
- For multi-thread (system specs with JS), use **DatabaseCleaner** with `:truncation` strategy.
- Don't share state across examples via class-level mutable variables.
## Mocks & stubs
- `instance_double(SomeClass, method: value)` — verifies the doubled class actually has the method.
- `allow(...).to receive(...)` for stubs (no expectation). `expect(...).to have_received(...)` for verification *after* the action.
- Don't mock the framework. If you mock `User.find`, you're testing at the wrong level.
## System specs
- Use Capybara's `expect(page).to have_content(...)` and `have_selector(...)` — they retry, accommodating async work.
- Avoid `sleep`. If retry-based matchers don't work, the page isn't ready and the test will be flaky.
- Run headless in CI; with a visible browser only when debugging locally.
## Speed
- Tag slow specs (`:slow`) and exclude from default runs.
- `before(:all)` is risky — state leaks across examples. Prefer `before(:each)`.
- Use `--profile 10` to find your slowest examples.
## Conventions
- One assertion per example where possible. Two when they're tightly related.
- `describe` for classes/methods, `context` for situational variants ("when admin", "when guest").
- Error messages are read from the matcher — don't add your own unless it adds info.
## Don't
- Don't load Rails for pure-Ruby unit tests. `spec_helper.rb` for those, `rails_helper.rb` for Rails-dependent.
- Don't hit external APIs in tests. Stub with **WebMock** + **VCR** (or just WebMock for predictable scenarios).
- Don't test private methods directly. They're tested through the public surface.
- Don't disable specs. Either fix or delete.
Other Ruby on Rails templates
Modern Rails Rules
Rails 7+ best practices: conventions, generators, and the Omakase stack.
Rails + Hotwire (Stimulus + Turbo)
Server-rendered frontends with Turbo Frames, Streams, and Stimulus.
Rails ActiveRecord Patterns
Model design, scopes, callbacks, concerns, N+1 prevention.
Rails Deployment (Kamal + Docker)
Deploy Rails with Kamal, Docker, and the Solid Queue/Cable stack.