rspec-rails
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.
What this skill does
# RSpec Rails
rspec-rails integrates RSpec with Rails, providing specialized spec types for each Rails component.
## Spec Types
| 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 |
## Model Specs
Test ActiveRecord models, validations, associations, and scopes:
```ruby
# 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
```
### Shoulda Matchers
Add to Gemfile for one-liner validations:
```ruby
# 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
```
## Request Specs
Test HTTP endpoints (preferred over controller specs):
```ruby
# 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: "[email protected]", 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
```
### JSON API Testing
```ruby
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: "[email protected]" }.to_json,
headers: headers
expect(response).to have_http_status(:created)
end
end
end
```
## System Specs
Browser-based tests using Capybara:
```ruby
# 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: "[email protected]"
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
```
### JavaScript Testing
```ruby
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
```
### Capybara Matchers
```ruby
# 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)
```
## Mailer Specs
```ruby
# spec/mailers/user_mailer_spec.rb
RSpec.describe UserMailer, type: :mailer do
describe "#welcome" do
let(:user) { create(:user, email: "[email protected]") }
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(["[email protected]"])
expect(mail.from).to eq(["[email protected]"])
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
```
### Testing Email Delivery
```ruby
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
```
## Job Specs
```ruby
# 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
Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.