This skill guides writing comprehensive RSpec tests for Ruby and Rails applications. Use when creating spec files, writing test cases, or testing new features. Covers RSpec syntax, describe/context organization, subject/let patterns, fixtures, mocking with allow/expect, and shoulda matchers.
Writes comprehensive RSpec tests using AAA pattern, fixtures, and shoulda matchers. Triggers when creating spec files or testing new features.
/plugin marketplace add majesticlabs-dev/majestic-marketplace/plugin install majestic-rails@majestic-marketplaceThis skill is limited to using the following tools:
resources/anti-patterns.mdresources/matchers.mdresources/patterns.mdrequire '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 tests
├── services/ # Service object tests
├── controllers/ # Controller tests
├── requests/ # Request specs (API testing)
├── mailers/ # Mailer tests
├── jobs/ # Background job tests
├── fixtures/ # Test data
├── support/ # Helper modules and shared examples
└── rails_helper.rb # Rails-specific configuration
describe 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 resources/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 resources/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 resources/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 resources/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 resources/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
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.