From rails
This skill should be used when the user wants to write, scaffold, or improve tests in a Ruby on Rails application. Covers RSpec organization, FactoryBot patterns, request/model/service specs, VCR setup for HTTP stubbing, and test-driven development workflows. Activates on: "write tests", "add specs", "rspec", "factory bot", "test this", "spec for", "scaffold test", "write spec", "missing tests", "test coverage", "VCR cassette", "stub http", "testing strategy", "TDD", "test driven", "rails test", "how to test", "scrivere test", "aggiungere spec".
npx claudepluginhub fabn/claude-plugins --plugin railsThis skill uses the workspace's default tool permissions.
Scaffold and write tests for Rails applications using RSpec, FactoryBot, shoulda-matchers, and VCR. Follows project conventions for test organization and patterns.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Scaffold and write tests for Rails applications using RSpec, FactoryBot, shoulda-matchers, and VCR. Follows project conventions for test organization and patterns.
Reference files: Consult reference/patterns.md for complete RSpec examples, FactoryBot patterns, and VCR configuration.
Before writing any test, discover the project's testing setup:
Gemfile for rspec-rails vs minitestfactory_bot_rails (standard) or fabricationvcr + webmock, or webmock aloneshoulda-matchers, rspec-itsbin/rspec wrapper or plain bundle exec rspecspec/support/ for shared contexts, custom matchers, and helpersspec/rails_helper.rb for included modules and configurationAlso check the project's CLAUDE.md for testing conventions.
Based on the user's request, identify:
| Source Code | Spec Type | Spec Location |
|---|---|---|
app/models/ | Model spec | spec/models/ |
app/services/ | Service spec | spec/services/ |
app/controllers/api/ | Request spec | spec/api/ or spec/requests/ |
app/graphql/mutations/ | GraphQL spec | spec/graphql/mutations/ |
app/graphql/queries/ | GraphQL spec | spec/graphql/queries/ |
app/jobs/ | Job spec | spec/jobs/ |
app/policies/ | Policy spec | spec/policies/ |
app/controllers/ | Request spec | spec/requests/ |
Read the source file to understand:
Existing spec: Search for a spec file matching the source:
Glob: spec/**/*<model_name>*_spec.rb
Existing factory: Search for the factory:
Glob: spec/factories/*<model_name>*.rb
Related specs: Find specs that test similar patterns in the project to match style:
Glob: spec/<type>/*_spec.rb (read 1-2 examples)
If a spec exists, read it first and add to it rather than creating a new file.
If the model doesn't have a factory yet, create one:
spec/factories/<plural_model_name>.rbsequence for unique fieldsassociation for belongs_totrait blocks for variations (states, roles, edge cases)Follow the project's existing factory style. See reference/patterns.md for examples.
Write the spec following these principles:
Structure:
describe for the class/method being testedcontext for different scenarios ("when valid", "when unauthorized")it with descriptive strings (not it { should ... } for complex behavior)For model specs:
it { is_expected.to belong_to(:user) }it { is_expected.to validate_presence_of(:name) }For service specs:
described_class.new(args).call or the primary method)For request specs:
let for setup, avoid before blocks when let sufficesFor job specs:
perform directlyWhen the code under test makes HTTP calls:
Check for VCR: If the project uses VCR, record cassettes:
it "fetches data", :vcr do
result = service.call
expect(result).to be_success
end
Cassettes are stored in spec/cassettes/ (or spec/fixtures/vcr_cassettes/)
If no VCR: Use webmock stubs:
stub_request(:get, "https://api.example.com/data")
.to_return(status: 200, body: { result: "ok" }.to_json)
For Sidekiq jobs: Use have_enqueued_sidekiq_job or test inline with Sidekiq::Testing.inline!
For email: Use ActionMailer::Base.deliveries or have_enqueued_mail
After writing the spec:
Run the specific spec:
bundle exec rspec spec/path/to/new_spec.rb
Check for failures and fix:
let/let! orderingtravel_to or freeze_timeRun RuboCop on the spec:
bundle exec rubocop spec/path/to/new_spec.rb
Suggest next steps:
| Situation | Action |
|---|---|
| No RSpec in project | Check for Minitest, suggest setup if neither exists |
| Factory not found | Create it based on model schema (db/schema.rb) |
| VCR cassette expired | Suggest re-recording with VCR_RECORD=all bundle exec rspec spec/path |
| Database schema mismatch | Suggest RAILS_ENV=test rails db:drop db:create db:schema:load |
| Flaky test (time-dependent) | Use travel_to(Time.zone.parse("2024-01-15 10:00")) block |
| Missing test database | Suggest RAILS_ENV=test rails db:prepare |
reference/patterns.md — Complete RSpec examples for models, services, requests, jobs, and policies with FactoryBot and VCR patterns