From ruby-rails
Comprehensive RSpec testing for Ruby and Rails applications. Covers model specs, request specs, system specs, factories, mocks, and TDD workflow. Automatically triggers on RSpec-related keywords and testing scenarios.
npx claudepluginhub el-feo/ai-context --plugin ruby-railsThis skill uses the workspace's default tool permissions.
Expert guidance for writing comprehensive tests in RSpec for Ruby and Rails applications. This skill provides immediate, actionable testing strategies with deep-dive references for complex scenarios.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Expert guidance for writing comprehensive tests in RSpec for Ruby and Rails applications. This skill provides immediate, actionable testing strategies with deep-dive references for complex scenarios.
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
describe '#full_name' do
it 'returns the first and last name' do
user = User.new(first_name: 'John', last_name: 'Doe')
expect(user.full_name).to eq('John Doe')
end
end
end
Key concepts:
describe: Groups related tests (classes, methods)context: Describes specific scenariosit: Individual test exampleexpect: Makes assertions using matchers# Run all specs
bundle exec rspec
# Run specific file
bundle exec rspec spec/models/user_spec.rb
# Run specific line
bundle exec rspec spec/models/user_spec.rb:12
# Run with documentation format
bundle exec rspec --format documentation
# Run only failures from last run
bundle exec rspec --only-failures
Test business logic, validations, associations, and methods:
RSpec.describe Article, type: :model do
# Test validations
describe 'validations' do
it { should validate_presence_of(:title) }
it { should validate_length_of(:title).is_at_most(100) }
end
# Test associations
describe 'associations' do
it { should belong_to(:author) }
it { should have_many(:comments) }
end
# Test instance methods
describe '#published?' do
context 'when publish_date is in the past' do
it 'returns true' do
article = Article.new(publish_date: 1.day.ago)
expect(article.published?).to be true
end
end
context 'when publish_date is in the future' do
it 'returns false' do
article = Article.new(publish_date: 1.day.from_now)
expect(article.published?).to be false
end
end
end
# Test scopes
describe '.recent' do
it 'returns articles from the last 30 days' do
old = create(:article, created_at: 31.days.ago)
recent = create(:article, created_at: 1.day.ago)
expect(Article.recent).to include(recent)
expect(Article.recent).not_to include(old)
end
end
end
Test HTTP requests and responses across the entire stack:
RSpec.describe 'Articles API', type: :request do
describe 'GET /articles' do
it 'returns all articles' do
create_list(:article, 3)
get '/articles'
expect(response).to have_http_status(:success)
expect(JSON.parse(response.body).size).to eq(3)
end
end
describe 'POST /articles' do
context 'with valid params' do
it 'creates a new article' do
article_params = { article: { title: 'New Article', body: 'Content' } }
expect {
post '/articles', params: article_params
}.to change(Article, :count).by(1)
expect(response).to have_http_status(:created)
end
end
context 'with invalid params' do
it 'returns errors' do
invalid_params = { article: { title: '' } }
post '/articles', params: invalid_params
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
describe 'authentication' do
it 'requires authentication for create' do
post '/articles', params: { article: { title: 'Test' } }
expect(response).to have_http_status(:unauthorized)
end
it 'allows authenticated users to create' do
user = create(:user)
post '/articles',
params: { article: { title: 'Test' } },
headers: { 'Authorization' => "Bearer #{user.token}" }
expect(response).to have_http_status(:created)
end
end
end
Test user workflows through the browser with Capybara:
RSpec.describe 'Article management', type: :system do
before { driven_by(:selenium_chrome_headless) }
scenario 'user creates an article' do
visit new_article_path
fill_in 'Title', with: 'My Article'
fill_in 'Body', with: 'Article content'
click_button 'Create Article'
expect(page).to have_content('Article was successfully created')
expect(page).to have_content('My Article')
end
scenario 'user edits an article' do
article = create(:article, title: 'Original Title')
visit article_path(article)
click_link 'Edit'
fill_in 'Title', with: 'Updated Title'
click_button 'Update Article'
expect(page).to have_content('Updated Title')
expect(page).not_to have_content('Original Title')
end
# Test JavaScript interactions
scenario 'user filters articles', js: true do
create(:article, title: 'Ruby Article', category: 'ruby')
create(:article, title: 'Python Article', category: 'python')
visit articles_path
select 'Ruby', from: 'filter'
expect(page).to have_content('Ruby Article')
expect(page).not_to have_content('Python Article')
end
end
Use FactoryBot for test data. Define factories in spec/factories/, use traits for variations, and prefer build over create when persistence isn't needed.
See references/factory_bot.md for factory definitions, traits, sequences, associations, and build strategies.
| Category | Examples |
|---|---|
| Equality | eq, eql, be, equal |
| Truthiness | be_truthy, be_falsy, be_nil, be_a |
| Collections | include, contain_exactly, match_array |
| Changes | change, raise_error, have_enqueued_job |
See references/matchers.md for the complete matcher reference.
Use double or instance_double (preferred — verifies against real class) for test doubles. Stub with allow(obj).to receive(:method), set expectations with expect(obj).to receive(:method), and verify after the fact with spies via have_received.
See references/mocking.md for test doubles, stubbing, message expectations, and spies.
Use let (lazy) and let! (eager) for test data, before hooks for shared setup, shared_examples for reusable test groups, and shared_context for reusable setup blocks. Prefer subject for the object under test.
See references/core_concepts.md for detailed coverage of hooks, let, shared examples, shared contexts, and subject.
describe User do
it 'has a full name' do
user = User.new(first_name: 'John', last_name: 'Doe')
expect(user.full_name).to eq('John Doe')
end
end
# Fails: undefined method `full_name'
class User
def full_name
"#{first_name} #{last_name}"
end
end
# Passes!
Start with system specs for user-facing features:
Drop to request specs for API/controller logic:
Use model specs for business logic:
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment'
abort("Run in production!") if Rails.env.production?
require 'rspec/rails'
# Auto-require support files
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
RSpec.configure do |config|
# Use transactional fixtures
config.use_transactional_fixtures = true
# Infer spec type from file location
config.infer_spec_type_from_file_location!
# Filter Rails backtrace
config.filter_rails_from_backtrace!
# Include FactoryBot methods
config.include FactoryBot::Syntax::Methods
# Include request helpers
config.include RequestHelpers, type: :request
# Capybara configuration for system specs
config.before(:each, type: :system) do
driven_by :selenium_chrome_headless
end
end
RSpec.configure do |config|
# Show detailed failure messages
config.example_status_persistence_file_path = "spec/examples.txt"
# Disable monkey patching (use expect syntax only)
config.disable_monkey_patching!
# Output warnings
config.warnings = true
# Profile slowest tests
config.profile_examples = 10 if ENV['PROFILE']
# Run specs in random order
config.order = :random
Kernel.srand config.seed
end
For testing background jobs, mailers, file uploads, pagination, and search, see references/rails_testing.md.
Use let instead of before for lazy loading
Avoid database calls when testing logic (use mocks)
Use build instead of create when persistence isn't needed
Use build_stubbed for non-persisted objects with associations
Tag slow tests and exclude them during development:
it 'slow test', :slow do
# test code
end
# Run with: rspec --tag ~slow
Most Common Commands:
rspec # Run all specs
rspec spec/models # Run model specs
rspec --tag ~slow # Exclude slow specs
rspec --only-failures # Rerun failures
rspec --format documentation # Readable output
rspec --profile # Show slowest specs
Most Common Matchers:
eq(expected) - value equalitybe_truthy / be_falsy - truthinessinclude(item) - collection membershipraise_error(Error) - exceptionschange { }.by(n) - state changesMost Common Stubs:
allow(obj).to receive(:method) - stub methodexpect(obj).to receive(:method) - expect calldouble('name', method: value) - create doubleFor detailed information on specific topics, see the references directory:
For debugging failing tests, testing complex queries, and testing callbacks, see references/rails_testing.md.