Install shoulda-matchers for RSpec
mainAdd shoulda-matchers to your Gemfile within the :test group and run bundle install.
group :test do
gem 'shoulda-matchers', '~> 8.0'
endrepository·main·Indexed 23 days ago
https://github.com/thoughtbot/shoulda-matchersA library providing RSpec and Minitest-compatible one-liners to test common Rails functionality. It reduces the complexity of manual testing for ActiveRecord, ActiveModel, ActionController, and Routing. Supported versions include Ruby 3.3+, Rails 7.2+, RSpec 3.x, and Minitest 5.x, with compatibility versions available for older environments.
Add shoulda-matchers to your Gemfile within the :test group and run bundle install.
group :test do
gem 'shoulda-matchers', '~> 8.0'
endThe subject is an implicit reference to the object under test. For validations, it is often better to provide a valid instance (e.g., using FactoryBot) rather than a fresh, invalid one.
Note: When overriding the subject, always provide an instance of the class under test. In controller tests, avoid using subject { get :index } if you need to use matchers like permit; instead, use before { get :index } and assert against the response object.
# RSpec with custom subject
RSpec.describe Post, type: :model do
describe 'validations' do
subject { build(:post) }
it { should validate_presence_of(:title) }
end
end
# RSpec Controller pattern to avoid subject issues
RSpec.describe PostsController, type: :controller do
describe 'GET #index' do
before { get :index }
it { expect(response).to have_http_status(:success) }
it { should render_template('index') }
end
endTo use shoulda-matchers with Minitest in a Rails application, add the following configuration to the bottom of test/test_helper.rb.
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :minitest
with.library :rails
end
endShoulda Matchers provides a one-liner syntax for testing Rails components using the should macro. It supports ActiveRecord models, ActiveModel models, ActionController, and Routing (RSpec only).
RSpec.describe MenuItem, type: :model do
describe 'associations' do
it { should belong_to(:category).class_name('MenuCategory') }
end
describe 'validations' do
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name).scoped_to(:category_id) }
end
endclass MenuItemTest < ActiveSupport::TestCase
context 'associations' do
should belong_to(:category).class_name('MenuCategory')
end
context 'validations' do
should validate_presence_of(:name)
should validate_uniqueness_of(:name).scoped_to(:category_id)
end
end# RSpec
RSpec.describe MenuItem, type: :model do
describe 'associations' do
it { should belong_to(:category).class_name('MenuCategory') }
end
describe 'validations' do
it { should validate_presence_of(:name) }
it { should validate_uniqueness_of(:name).scoped_to(:category_id) }
end
end
# Minitest (Shoulda)
class MenuItemTest < ActiveSupport::TestCase
context 'associations' do
should belong_to(:category).class_name('MenuCategory')
end
context 'validations' do
should validate_presence_of(:name)
should validate_uniqueness_of(:name).scoped_to(:category_id)
end
endIf you are using RSpec with ActiveRecord or ActiveModel in a non-Rails project, add this configuration to the bottom of spec/spec_helper.rb to enable the appropriate libraries.
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
# Keep as many of these lines as are necessary:
with.library :active_record
with.library :active_model
end
endIf you are using Minitest with ActiveRecord or ActiveModel in a non-Rails project, add this configuration to the bottom of test/test_helper.rb.
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :minitest
# Keep as many of these lines as are necessary:
with.library :active_record
with.library :active_model
end
endIf you are using the shoulda umbrella gem, ensure it is updated to the latest version. Otherwise, add shoulda-matchers directly to your Gemfile in the :test group and run bundle install.
group :test do
gem 'shoulda-matchers', '~> 8.0'
endIn Rails projects, matchers are automatically available in specific example groups based on tags (e.g., type: :model). In non-Rails projects, you must manually include the matcher modules.
To include them in a specific group:
RSpec.describe MySpecialModel do
include Shoulda::Matchers::ActiveModel
include Shoulda::Matchers::ActiveRecord
endTo configure them globally for all groups with a specific tag (e.g., in rails_helper.rb):
RSpec.configure do |config|
config.include(Shoulda::Matchers::ActiveModel, type: :model)
config.include(Shoulda::Matchers::ActiveRecord, type: :model)
endTo use shoulda-matchers with RSpec in a Rails application, add the following configuration to the bottom of spec/rails_helper.rb (or a support file). This tells the gem to integrate with both the RSpec test framework and the Rails library.
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
endTo use shoulda-matchers, you must configure it to integrate with your chosen test framework (e.g., :rspec) and libraries (e.g., :rails). This is done using the Shoulda::Matchers.configure block and the integrate method. If no framework or library is specified, a ConfigurationError will be raised.
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
endThe validate_confirmation_of matcher tests the usage of the validates_confirmation_of validation in ActiveModel. It can be used with both RSpec and Minitest (Shoulda).
RSpec
RSpec.describe User, type: :model do
it { should validate_confirmation_of(:email) }
endMinitest
class UserTest < ActiveSupport::TestCase
should validate_confirmation_of(:email)
end.on(context)Use .on if your validation applies only under a specific context (e.g., :create).
RSpec
it { should validate_confirmation_of(:password).on(:create) }Minitest
should validate_confirmation_of(:password).on(:create).with_message(message)Use .with_message if you are using a custom validation message.
RSpec
it do
should validate_confirmation_of(:password).with_message('Please re-enter your password')
endMinitest
should validate_confirmation_of(:password).with_message('Please re-enter your password')You can pass multiple attributes to the matcher to assert that each one has the validation. Any qualifier chained to the matcher is applied to every attribute uniformly.
RSpec
it { should validate_confirmation_of(:password, :email) }Minitest
should validate_confirmation_of(:password, :email)class User
include ActiveModel::Model
attr_accessor :email
validates_confirmation_of :email
end
# RSpec
RSpec.describe User, type: :model do
it { should validate_confirmation_of(:email) }
end
# Minitest (Shoulda)
class UserTest < ActiveSupport::TestCase
should validate_confirmation_of(:email)
endWhile should is the preferred shorthand for Shoulda Matchers, you can use RSpec's is_expected.to syntax if preferred.
RSpec.describe Person, type: :model do
it { is_expected.to validate_presence_of(:name) }
end