Help us improve
Share bugs, ideas, or general feedback.
From majestic-rails
Writes RSpec tests for Ruby and Rails apps using AAA pattern, describe/context blocks, subject/let, fixtures, allow/expect mocking, and shoulda matchers. Use for spec files, test cases, or new features.
npx claudepluginhub majesticlabs-dev/majestic-marketplace --plugin majestic-railsHow this skill is triggered — by the user, by Claude, or both
Slash command
/majestic-rails:rspec-coderThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
- **AAA Pattern**: Arrange-Act-Assert structure for clarity
Writes, fixes, and refactors RSpec tests for Ruby/Rails apps using describe/it blocks, expect matchers, test doubles, FactoryBot, and TDD best practices. Activates on spec files or test queries.
Assists writing, reviewing, and improving RSpec tests for Ruby on Rails apps including model, controller, system, and integration specs using Better Specs and thoughtbot best practices.
Guides RSpec testing for Ruby and Rails apps covering model specs, request specs, system specs, factories, mocks, and TDD workflow. Triggers on RSpec keywords and testing scenarios.
Share bugs, ideas, or general feedback.
require 'rails_helper'RSpec imports via .rspec config. Adding manually is redundant.
# ✅ GOOD - no require needed
RSpec.describe User do
# ...
end
RSpec infers type from file location automatically.
# ✅ GOOD - type inferred from spec/models/ location
RSpec.describe User do
# ...
end
::# ✅ GOOD - no leading double colons
RSpec.describe DynamicsGp::ERPSynchronizer do
# ...
end
spec/models/ - Model unit testsspec/services/ - Service object testsspec/controllers/ - Controller testsspec/requests/ - Request specs (API testing)spec/mailers/ - Mailer testsspec/jobs/ - Background job testsspec/fixtures/ - Test dataspec/support/ - Helper modules and shared examplesspec/rails_helper.rb - Rails-specific configurationdescribe and context| Block | Purpose | Example |
|---|---|---|
describe | Groups by method/class | describe "#process" |
context | Groups by condition | context "when user is admin" |
RSpec.describe OrderProcessor do
describe "#process" do
context "with valid payment" do
# success tests
end
context "with invalid payment" do
# failure tests
end
end
end
See references/patterns.md for detailed examples.
| Pattern | Use Case |
|---|---|
subject(:name) { ... } | Primary object/method under test |
let(:name) { ... } | Lazy-evaluated, memoized data |
let!(:name) { ... } | Eager evaluation (before each test) |
RSpec.describe User do
describe "#full_name" do
subject(:full_name) { user.full_name }
let(:user) { users(:alice) }
it { is_expected.to eq("Alice Smith") }
end
end
See references/patterns.md for detailed examples.
# spec/fixtures/users.yml
alice:
name: Alice Smith
email: alice@example.com
admin: false
RSpec.describe User do
fixtures :users
it "validates email" do
expect(users(:alice)).to be_valid
end
end
See references/patterns.md for detailed examples.
| Method | Purpose |
|---|---|
allow(obj).to receive(:method) | Stub return value |
expect(obj).to receive(:method) | Verify call happens |
# Stubbing external service
allow(PaymentGateway).to receive(:charge).and_return(true)
# Verifying method called
expect(UserMailer).to receive(:welcome_email).with(user)
See references/matchers.md for complete reference.
# Equality
expect(value).to eq(expected)
# Truthiness
expect(obj).to be_valid
expect(obj).to be_truthy
# Change
expect { action }.to change { obj.status }.to("completed")
expect { action }.to change(Model, :count).by(1)
# Errors
expect { action }.to raise_error(SomeError)
# Collections
expect(array).to include(item)
expect(array).to be_empty
# Validations
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_uniqueness_of(:email) }
# Associations
it { is_expected.to have_many(:posts) }
it { is_expected.to belong_to(:account) }
Structure all tests as Arrange-Act-Assert:
describe "#process_refund" do
subject(:process_refund) { processor.process_refund }
let(:order) { orders(:completed_order) }
let(:processor) { described_class.new(order) }
it "updates order status" do
process_refund # Act
expect(order.reload.status).to eq("refunded") # Assert
end
it "credits user account" do
expect { process_refund } # Act
.to change { order.user.reload.account_balance } # Assert
.by(order.total)
end
end
| Type | Test For |
|---|---|
| Models | Validations, associations, scopes, callbacks, methods |
| Services | Happy path, sad path, edge cases, external integrations |
| Controllers | Status codes, response formats, auth, redirects |
| Jobs | Execution, retry logic, error handling, idempotency |
RSpec.describe User do
fixtures :users
describe "validations" do
subject(:user) { users(:valid_user) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
end
describe "associations" do
it { is_expected.to have_many(:posts).dependent(:destroy) }
end
describe "#full_name" do
subject(:full_name) { user.full_name }
let(:user) { User.new(first_name: "Alice", last_name: "Smith") }
it { is_expected.to eq("Alice Smith") }
context "when last name is missing" do
let(:user) { User.new(first_name: "Alice") }
it { is_expected.to eq("Alice") }
end
end
end
See references/anti-patterns.md for detailed examples.
| Anti-Pattern | Why Bad |
|---|---|
require 'rails_helper' | Redundant, loaded via .rspec |
type: :model | Redundant, inferred from location |
Leading :: in namespace | Violates RuboCop style |
| Empty test bodies | False confidence |
| Testing private methods | Couples to implementation |
| Not using fixtures | Slow tests |
| Not using shoulda | Verbose validation tests |
Critical Conventions:
require 'rails_helper'::Test Organization:
describe for methods/classescontext for conditionsTest Data:
let for lazy datasubject for method under testAssertions:
change matcher for state changesCoverage:
# Minimal spec file
RSpec.describe User do
fixtures :users
describe "#full_name" do
subject(:full_name) { user.full_name }
let(:user) { users(:alice) }
it { is_expected.to eq("Alice Smith") }
end
describe "validations" do
subject(:user) { users(:alice) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to have_many(:posts) }
end
end