npx claudepluginhub hoblin/claude-ruby-marketplace --plugin rspecThis skill uses the workspace's default tool permissions.
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.
examples/core/basic_structure.rbexamples/core/configuration.rbexamples/core/hooks.rbexamples/core/memoized_helpers.rbexamples/core/metadata_filtering.rbexamples/core/shared_examples.rbexamples/factory_bot/associations.rbexamples/factory_bot/build_strategies.rbexamples/factory_bot/callbacks.rbexamples/factory_bot/custom_construction.rbexamples/factory_bot/factory_definition.rbexamples/factory_bot/inheritance.rbexamples/factory_bot/traits.rbexamples/factory_bot/transients.rbexamples/matchers/change.rbexamples/matchers/collections.rbexamples/matchers/comparisons.rbexamples/matchers/composing.rbexamples/matchers/custom_matchers.rbexamples/matchers/equality.rbProvides Ktor server patterns for routing DSL, plugins (auth, CORS, serialization), Koin DI, WebSockets, services, and testApplication testing.
Conducts multi-source web research with firecrawl and exa MCPs: searches, scrapes pages, synthesizes cited reports. For deep dives, competitive analysis, tech evaluations, or due diligence.
Provides demand forecasting, safety stock optimization, replenishment planning, and promotional lift estimation for multi-location retailers managing 300-800 SKUs.
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.
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
| 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 |
describe "#calculate_total" do
subject(:total) { order.calculate_total }
it "returns sum of items" do
expect(total).to eq(100)
end
end
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
expect(x).to eq(y) # ==
expect(x).to eql(y) # eql? (type-sensitive)
expect(x).to be(y) # equal? (identity)
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
expect(x).to be > 3
expect(x).to be_between(1, 10).inclusive
expect(x).to be_within(0.1).of(3.14)
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)
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)
expect { raise "boom" }.to raise_error
expect { raise ArgumentError, "bad" }.to raise_error(ArgumentError, /bad/)
expect([]).to be_empty # [].empty?
expect(user).to be_valid # user.valid?
expect(hash).to have_key(k) # hash.has_key?(k)
# 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")
allow(user).to receive(:name).and_return("Bob")
allow(Api).to receive(:fetch).and_return(data)
allow(obj).to receive(:method) { computed_value }
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")
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))
| Spec Type | Use For | Key Helpers |
|---|---|---|
type: :model | Business logic, scopes, validations | build, create, associations |
type: :request | Controller actions (preferred) | get, post, response, have_http_status |
type: :system | Browser/UI testing | visit, fill_in, click_button, have_text |
type: :job | Background jobs | have_enqueued_job, perform_now |
type: :mailer | Email delivery | have_enqueued_mail, deliver_now |
type: :routing | Route resolution | route_to, be_routable |
See examples/rails/ for complete spec templates.
described_class instead of hardcoding class namelet for test data, let! when database records must exist before testsubject(:user) { ... }instance_double) over plain doubleit "creates user" not it "should create user"build_stubbed or build over create when database not needed@user) - use let for type safetybefore just to trigger let evaluation - use let! instead:
# 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 sign_in(user) or driven_by(:rack_test)let! when let suffices (wastes resources)any_instance_of - prefer stubbing ClassName.new to return a double (see references/mocks.md)receive_message_chain (violates Law of Demeter)# 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 testscreate - Database queries, scopes, associationsattributes_for - Controller paramsSee references/factory_bot.md for traits, sequences, associations, and callbacks.
Essential settings for spec_helper.rb and rails_helper.rb:
| Setting | Purpose |
|---|---|
verify_partial_doubles = true | Validates stubbed methods exist |
filter_run_when_matching :focus | Run only focused specs (fit, fdescribe) |
order = :random | Randomize spec order to catch dependencies |
use_transactional_fixtures = true | Rollback database after each spec |
infer_spec_type_from_file_location! | Auto-detect spec type from path |
See examples/core/configuration.rb for complete setup.
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/`
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
rspec --seed 12345 # reproduce random order
rspec --fail-fast # stop on first failure
rspec --backtrace # full backtrace