Loading...
Loading...
Use when writing, reviewing, or cleaning up RSpec tests for Ruby and Rails codebases. Covers spec type selection, factory design, flaky test fixes, shared examples, deterministic assertions, test-driven development discipline, and choosing the best first failing spec for Rails changes. Also applies when choosing between model, request, system, and job specs.
npx skill4agent add igmarin/rails-agent-skills rspec-best-practices| Aspect | Rule |
|---|---|
| Spec type | Request > controller; model for domain; system only for critical E2E |
| Assertions | Test behavior, not implementation |
| Factories | Minimal — only attributes needed for the test |
| Mocking | Stub external boundaries, not internal code |
| Isolation | Each example independent; no shared mutable state |
| Naming | |
| Service specs | Required: |
| Default to |
| External service mocking | Class methods: |
| Example names | Never use "and" in an example name — one behavior per example; split it |
| First slice | Start at the highest-value boundary that proves behavior |
| TDD | Write test first, run it, verify failure, then implement |
THE WORKFLOW IS: PRD → TASKS → TESTS → IMPLEMENTATION
Tests are a GATE between planning and code.
NO implementation code may be written until:
1. The test EXISTS
2. The test has been RUN
3. The test FAILS for the correct reason (feature missing, not typo)rails-tdd-slices| Change type | Best first spec |
|---|---|
| New endpoint, controller action, or API behavior | Request spec |
| New domain rule on an existing model | Model spec |
| New service object or orchestration flow | Service spec |
| Background job behavior | Job spec; add service/domain spec if logic is non-trivial |
| Rails engine route, install, or generator behavior | Engine request/routing/generator spec via |
| Bug fix | Reproduction spec at the boundary where the bug is observed |
spec/app/models/user.rbspec/models/user_spec.rbspec/support/let_it_betest-proftravel_toTime.nowtravel_tolet(:subscription) { create(:subscription, activated_at: Time.current) }
context 'after expiration' do
it 'is expired' do
travel_to 31.days.from_now do
expect(subscription).to be_expired
end
end
end# frozen_string_literal: true
RSpec.describe 'POST /orders', type: :request do
let(:product) { create(:product, stock: 5) }
context 'when product is in stock' do
it 'returns 201 for an in-stock product' do
post orders_path, params: { order: { product_id: product.id } }, as: :json
expect(response).to have_http_status(:created)
end
end
endrails-engine-testing| Pitfall | What to do |
|---|---|
| Starting with the lowest layer by habit | Begin at the boundary that proves the behavior users care about |
| Testing mock behavior instead of real behavior | Assert outcomes, not implementation details |
Recommending | Only use it when |
| Factories creating large graphs by default | Minimal factories — only what the test needs |
Setting dates in the past instead of | Always use |
| Code written before the test | Delete it. Reproduction step isn't done yet. |
| Test name contains "and" | One behavior per example. Split it. |
| Skill | When to chain |
|---|---|
| rails-tdd-slices | When the hardest part is choosing the first failing Rails spec or vertical slice |
| rails-bug-triage | When a bug report must be turned into a reproducible failing spec and fix plan |
| rspec-service-testing | For service object specs — |
| rails-engine-testing | For engine specs — dummy app, routing specs, generator specs |
| rails-code-review | When reviewing test quality as part of code review |
| refactor-safely | When adding characterization tests before refactoring |