Help us improve
Share bugs, ideas, or general feedback.
From rspec
This skill should be used when the user asks about "request specs", "system specs", "model specs", "controller specs", "mailer specs", "feature specs", "Capybara", "rspec-rails", or needs guidance on testing Rails applications with RSpec.
npx claudepluginhub bastos/ruby-plugin-marketplace --plugin rspecHow this skill is triggered — by the user, by Claude, or both
Slash command
/rspec:rspec-railsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
rspec-rails integrates RSpec with Rails, providing specialized spec types for each Rails component.
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.
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.
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.
Share bugs, ideas, or general feedback.
rspec-rails integrates RSpec with Rails, providing specialized spec types for each Rails component.
| Spec Type | Location | Purpose |
|---|---|---|
| Model | spec/models/ | ActiveRecord models, validations, associations |
| Request | spec/requests/ | HTTP requests, full controller + routing |
| System | spec/system/ | Browser-based end-to-end tests |
| Mailer | spec/mailers/ | Email content and delivery |
| Job | spec/jobs/ | Background job behavior |
| View | spec/views/ | View rendering (less common) |
| Helper | spec/helpers/ | View helper methods |
| Routing | spec/routing/ | Route resolution |
| Controller | spec/controllers/ | Deprecated - use request specs |
Test ActiveRecord models, validations, associations, and scopes:
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
describe "validations" do
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_uniqueness_of(:email).case_insensitive }
it { is_expected.to validate_length_of(:password).is_at_least(8) }
end
describe "associations" do
it { is_expected.to have_many(:posts).dependent(:destroy) }
it { is_expected.to belong_to(:organization).optional }
it { is_expected.to have_one(:profile) }
end
describe "#full_name" do
it "returns first and last name" do
user = build(:user, first_name: "John", last_name: "Doe")
expect(user.full_name).to eq("John Doe")
end
end
describe ".active" do
it "returns only active users" do
active = create(:user, active: true)
inactive = create(:user, active: false)
expect(User.active).to include(active)
expect(User.active).not_to include(inactive)
end
end
end
Add to Gemfile for one-liner validations:
# Gemfile
group :test do
gem "shoulda-matchers"
end
# spec/rails_helper.rb
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
Test HTTP endpoints (preferred over controller specs):
# spec/requests/users_spec.rb
RSpec.describe "Users", type: :request do
describe "GET /users" do
it "returns a list of users" do
create_list(:user, 3)
get users_path
expect(response).to have_http_status(:ok)
expect(response.parsed_body.size).to eq(3)
end
end
describe "POST /users" do
context "with valid params" do
it "creates a user" do
user_params = { user: { email: "new@example.com", password: "password123" } }
expect {
post users_path, params: user_params
}.to change(User, :count).by(1)
expect(response).to redirect_to(user_path(User.last))
end
end
context "with invalid params" do
it "returns unprocessable entity" do
post users_path, params: { user: { email: "" } }
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
describe "with authentication" do
let(:user) { create(:user) }
it "allows access to authenticated users" do
sign_in user # Devise helper
get dashboard_path
expect(response).to have_http_status(:ok)
end
it "redirects unauthenticated users" do
get dashboard_path
expect(response).to redirect_to(login_path)
end
end
end
RSpec.describe "API::Users", type: :request do
describe "GET /api/users/:id" do
it "returns user as JSON" do
user = create(:user)
get api_user_path(user), headers: { "Accept" => "application/json" }
expect(response).to have_http_status(:ok)
expect(response.content_type).to include("application/json")
json = response.parsed_body
expect(json["email"]).to eq(user.email)
end
end
describe "POST /api/users" do
it "creates user with JSON params" do
headers = {
"Content-Type" => "application/json",
"Accept" => "application/json"
}
post api_users_path,
params: { email: "test@example.com" }.to_json,
headers: headers
expect(response).to have_http_status(:created)
end
end
end
Browser-based tests using Capybara:
# spec/system/user_registration_spec.rb
RSpec.describe "User Registration", type: :system do
before do
driven_by(:rack_test) # Fast, no JS
# driven_by(:selenium_chrome_headless) # With JS
end
it "allows new user to register" do
visit new_user_registration_path
fill_in "Email", with: "newuser@example.com"
fill_in "Password", with: "password123"
fill_in "Password confirmation", with: "password123"
click_button "Sign up"
expect(page).to have_content("Welcome!")
expect(page).to have_current_path(dashboard_path)
end
it "shows validation errors" do
visit new_user_registration_path
fill_in "Email", with: ""
click_button "Sign up"
expect(page).to have_content("Email can't be blank")
end
end
RSpec.describe "Dynamic Form", type: :system, js: true do
before do
driven_by(:selenium_chrome_headless)
end
it "adds items dynamically" do
visit new_order_path
click_button "Add Item"
within "#items" do
expect(page).to have_selector(".item", count: 1)
end
end
it "filters results without page reload" do
create(:product, name: "Widget")
create(:product, name: "Gadget")
visit products_path
fill_in "Search", with: "Wid"
expect(page).to have_content("Widget")
expect(page).not_to have_content("Gadget")
end
end
# Content
expect(page).to have_content("Welcome")
expect(page).to have_text("Hello", exact: true)
expect(page).not_to have_content("Error")
# Elements
expect(page).to have_selector("div.alert")
expect(page).to have_css(".user-list li", count: 3)
expect(page).to have_xpath("//table/tr")
# Forms
expect(page).to have_field("Email")
expect(page).to have_button("Submit")
expect(page).to have_checked_field("Remember me")
# Links
expect(page).to have_link("Home", href: root_path)
# Current URL
expect(page).to have_current_path(dashboard_path)
# spec/mailers/user_mailer_spec.rb
RSpec.describe UserMailer, type: :mailer do
describe "#welcome" do
let(:user) { create(:user, email: "test@example.com") }
let(:mail) { described_class.welcome(user) }
it "renders the headers" do
expect(mail.subject).to eq("Welcome to Our App!")
expect(mail.to).to eq(["test@example.com"])
expect(mail.from).to eq(["noreply@example.com"])
end
it "renders the body" do
expect(mail.body.encoded).to include("Hello")
expect(mail.body.encoded).to include(user.first_name)
end
it "includes unsubscribe link" do
expect(mail.body.encoded).to include(unsubscribe_url)
end
end
end
RSpec.describe UserRegistration do
it "sends welcome email" do
expect {
described_class.call(user_params)
}.to change { ActionMailer::Base.deliveries.count }.by(1)
end
it "enqueues welcome email" do
expect {
described_class.call(user_params)
}.to have_enqueued_mail(UserMailer, :welcome)
end
end
# spec/jobs/send_newsletter_job_spec.rb
RSpec.describe SendNewsletterJob, type: :job do
describe "#perform" do
it "sends newsletter to all subscribers" do
subscribers = create_list(:user, 3, subscribed: true)
newsletter = create(:newsletter)
expect {
described_class.perform_now(newsletter.id)
}.to change { ActionMailer::Base.deliveries.count }.by(3)
end
end
describe "enqueuing" do
it "enqueues job" do
expect {
described_class.perform_later(1)
}.to have_enqueued_job(described_class).with(1)
end
it "enqueues to correct queue" do
expect {
described_class.perform_later(1)
}.to have_enqueued_job.on_queue("mailers")
end
end
end
# spec/rails_helper.rb
require "spec_helper"
ENV["RAILS_ENV"] ||= "test"
require_relative "../config/environment"
abort("Production!") if Rails.env.production?
require "rspec/rails"
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
RSpec.configure do |config|
config.fixture_paths = [Rails.root.join("spec/fixtures")]
config.use_transactional_fixtures = true
config.infer_spec_type_from_file_location!
config.filter_rails_from_backtrace!
end
references/system-spec-patterns.md - Advanced Capybara patternsreferences/request-spec-patterns.md - API testing patternsexamples/full_request_spec.rb - Complete request spec exampleexamples/system_spec_with_js.rb - JavaScript testing example