smithery.ai

rspec

>- This skill should be used when the user asks to "write specs", "create spec", "add RSpec tests", "fix failing spec", or mentions RSpec, describe blocks, it blocks, expect syntax, test doubles, or matchers. Should also be used when editing *_spec.rb files, working in spec/ directory, planning implementation phases that include tests (TDD/RGRC workflow), writing Testing Strategy or Success Criteria sections, discussing unit or integration tests, or reviewing spec output and test failures. Comp…

First seen Mar 21, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.1.2

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 10,227 B
  • docs SUMMARY.md 607 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 4 installs

SKILL.md

RSpec Testing

This skill provides comprehensive guidance for writing effective RSpec tests in Ruby and Rails applications. Use for writing new specs, fixing failing tests, understanding matchers, using test doubles, and following RSpec best practices.

Quick Reference

Basic Structure

RSpec.describe Order do
  subject(:order) { described_class.new(items) }
  let(:items) { [item1, item2] }
  let(:item1) { double("item", price: 10) }
  let(:item2) { double("item", price: 20) }

  describe "#total" do
    it "sums item prices" do
      expect(order.total).to eq(30)
    end
  end

  context "with discount" do
    let(:order) { described_class.new(items, discount: 5) }

    it "applies discount" do
      expect(order.total).to eq(25)
    end
  end
end

Key Concepts

Concept Purpose
describe / context Group related examples
it / specify Define individual test cases
let Lazy-evaluated, memoized helper
let! Eager-evaluated helper (runs before each example)
subject Primary object under test
before / after Setup and teardown hooks
expect Make assertions

Writing Good Specs

Use Named Subject for Method Tests

describe "#calculate_total" do
  subject(:total) { order.calculate_total }

  it "returns sum of items" do
    expect(total).to eq(100)
  end
end

Context Blocks for Different States

describe "#withdraw" do
  context "with sufficient funds" do
    let(:account) { build(:account, balance: 100) }

    it "reduces balance" do
      expect { account.withdraw(50) }.to change(account, :balance).by(-50)
    end
  end

  context "with insufficient funds" do
    let(:account) { build(:account, balance: 10) }

    it "raises error" do
      expect { account.withdraw(50) }.to raise_error(InsufficientFunds)
    end
  end
end

Common Matchers

Equality

expect(x).to eq(y)          # ==
expect(x).to eql(y)         # eql? (type-sensitive)
expect(x).to be(y)          # equal? (identity)

Truthiness

expect(x).to be_truthy      # not nil or false
expect(x).to be_falsey      # nil or false
expect(x).to be_nil
expect(x).to be true        # exactly true

Comparisons

expect(x).to be > 3
expect(x).to be_between(1, 10).inclusive
expect(x).to be_within(0.1).of(3.14)

Collections

expect(arr).to include(1, 2)
expect(arr).to contain_exactly(3, 2, 1)  # order-independent
expect(arr).to all(be_positive)
expect(str).to start_with("hello")
expect(hash).to have_key(:name)

Changes

expect { x += 1 }.to change { x }.by(1)
expect { x += 1 }.to change { x }.from(0).to(1)
expect { user.save }.to change(User, :count).by(1)

Errors

expect { raise "boom" }.to raise_error
expect { raise ArgumentError, "bad" }.to raise_error(ArgumentError, /bad/)

Predicates (Dynamic)

expect([]).to be_empty      # [].empty?
expect(user).to be_valid    # user.valid?
expect(hash).to have_key(k) # hash.has_key?(k)

Test Doubles

Types

# Basic double (strict)
user = double("user", name: "Bob")

# Verifying doubles (recommended)
user = instance_double("User", name: "Bob")  # validates instance methods
api = class_double("Api", fetch: data)       # validates class methods
logger = object_double(Rails.logger)         # validates object methods

# Spy (null object for after-the-fact verification)
notifier = spy("notifier")

Stubbing

allow(user).to receive(:name).and_return("Bob")
allow(Api).to receive(:fetch).and_return(data)
allow(obj).to receive(:method) { computed_value }

Expectations

expect(user).to receive(:save).and_return(true)
expect(Api).to receive(:post).with(hash_including(id: 1))

# Spy pattern (verify after action)
notifier = spy("notifier")
service.call(notifier)
expect(notifier).to have_received(:notify).with("done")

Argument Matchers

expect(obj).to receive(:call).with(anything)
expect(obj).to receive(:call).with(kind_of(Integer))
expect(obj).to receive(:call).with(hash_including(a: 1))
expect(obj).to receive(:call).with(array_including(1, 2))

Rails Specs

Spec Type Use For Key Helpers
type: :model Business logic, scopes, validations build, create, associations
type: :request Controller actions (preferred) get, post, response, havehttpstatus
type: :system Browser/UI testing visit, fillin, clickbutton, have_text
type: :job Background jobs haveenqueuedjob, perform_now
type: :mailer Email delivery haveenqueuedmail, deliver_now
type: :routing Route resolution routeto, beroutable

See examples/rails/ for complete spec templates.

Best Practices

Do

  • Use described_class instead of hardcoding class name
  • Use let for test data, let! when database records must exist before test
  • Use named subject when referencing in tests: subject(:user) { ... }
  • Use context blocks to organize different scenarios
  • Use verifying doubles (instance_double) over plain double
  • Name examples with verbs: it "creates user" not it "should create user"
  • Keep examples focused on one behavior
  • Use factories over fixtures for flexible test data
  • Prefer build_stubbed or build over create when database not needed

Don't

  • Don't use instance variables (@user) - use let for type safety
  • Don't use before just to trigger let evaluation - use let! instead:

```ruby # BAD - before just to initialize let(:user) { create(:user) } before { user }

# GOOD - let! for eager evaluation let!(:user) { create(:user) } `` Use before for side-effects like signin(user) or drivenby(:rack_test)`

  • Don't use let! when let suffices (wastes resources)
  • Don't test Rails framework (validations work, focus on business logic)
  • Don't stub the object under test
  • Avoid anyinstanceof - prefer stubbing ClassName.new to return a double (see references/mocks.md)
  • Don't use receivemessagechain (violates Law of Demeter)
  • Don't write examples without descriptions
  • Shared examples work best for testing concerns across including classes. For unique behaviors, prefer repetition over abstraction

Factory Bot

# Build strategies
user = build(:user)              # In-memory, not persisted
user = create(:user)             # Persisted to database
user = build_stubbed(:user)      # Fake persisted (fastest)
attrs = attributes_for(:user)    # Hash of attributes

# With traits and attributes
user = create(:user, :admin, :verified, name: "Bob")

# Lists
users = create_list(:user, 5, :admin)

Strategy Selection:

  • build_stubbed - Unit tests without database (fastest)
  • build - Validation tests, method tests
  • create - Database queries, scopes, associations
  • attributes_for - Controller params

See references/factory_bot.md for traits, sequences, associations, and callbacks.

Configuration

Essential settings for spechelper.rb and railshelper.rb:

Setting Purpose
verifypartialdoubles = true Validates stubbed methods exist
filterrunwhen_matching :focus Run only focused specs (fit, fdescribe)
order = :random Randomize spec order to catch dependencies
usetransactionalfixtures = true Rollback database after each spec
inferspectypefromfile_location! Auto-detect spec type from path

See examples/core/configuration.rb for complete setup.

Before You Write

What are you about to do?

├── Creating or modifying a factory?
│   └── Read `references/factory_bot.md`
│
├── Writing a new spec file?
│   ├── Model/service/PORO → Read `references/core.md`
│   ├── Request/controller → Read `references/rails.md`
│   └── System/feature/job/mailer → Read `references/rails.md`
│
├── Using test doubles, stubs, or mocks?
│   └── Read `references/mocks.md`
│
├── Writing custom or complex matchers?
│   └── Read `references/matchers.md`
│
└── Fixing a failing spec?
    ├── Factory-related error → Read `references/factory_bot.md`
    ├── Mock/stub error → Read `references/mocks.md`
    ├── Matcher error → Read `references/matchers.md`
    └── Rails-specific error → Read `references/rails.md`

Need code examples?

├── Basic spec structure, hooks, shared examples
│   └── See `examples/core/`
│
├── Matcher usage patterns
│   └── See `examples/matchers/`
│
├── Test doubles and stubbing
│   └── See `examples/mocks/`
│
├── Rails spec templates (model, request, system, job, mailer)
│   └── See `examples/rails/`
│
└── Factory definitions with traits and associations
    └── See `examples/factory_bot/`

Running Specs

rspec                           # all specs
rspec spec/models               # directory
rspec spec/user_spec.rb         # file
rspec spec/user_spec.rb:23      # line
rspec --format doc              # documentation format
rspec --only-failures           # re-run failures
rspec --profile 10              # show slowest

Debugging

rspec --seed 12345              # reproduce random order
rspec --fail-fast               # stop on first failure
rspec --backtrace               # full backtrace