Using Shoulda Matchers with RSpec

A guide to setting up Shoulda Matchers to work with RSpec

Shoulda Matchers is a great tool to use to create one line tests for common functionality in Rails apps. I use them to test associations and validations within my Rails apps.

So if we have a model like this:

models/article.rb

class Article < ApplicationRecord
  validates :title, presence: true
end

Instead of doing this:

spec/models/article_spec.rb

RSpec.describe Article, type: :model do
  subject {
    described_class.new(title: 'Article Title')
  }

  it 'is valid with valid attributes' do
    expect(subject).to be_valid
  end

  it 'is not valid without a title' do
    subject.title = nil
    expect(subject).to_not be_valid
  end
end

We can use Shoulda Matchers to write this:

spec/models/article_spec.rb

RSpec.describe Article, type: :model do
  describe 'Validations' do
    it { should validate_presence_of(:title) }
  end
end

These one liners are less error prone and easier to write.

So onto the setup! First of all we will want to add the Shoulda Matchers gem to our Gemfile in the test block:

Gemfile

group :test do
  gem 'should-matchers'
end

Then we will want to run bundle install to install the dependency within our project.

I use Shoulda Matchers within my Rails projects and this is how I set it up:

spec/rails_helper.rb

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

Shoulda Matchers can be set up to work with other test frameworks and non Rails apps.

Shoulda Matchers should now be set up to work within your project.

As I mentioned above I use Shoulda Matchers to test my Rails associations and validations. So we can have ‘simple’ tests like:

RSpec.describe Article, type: :model do
  describe 'Associations' do
    it { should belong_to(:user) }
    it { should have_many(:comments) }
  end

  describe 'Validations' do
    it { should validate_presence_of(:title) }
  end
end

Occasionally you will have to build a valid object before using a matcher (Shoulda Matchers by default creates fresh models), for instance when validating uniqueness on some types of object:

describe 'Validations' do
  describe 'name uniqueness' do
    subject { build(:article) } # We use FactoryBot to build instances of our models
    it { should validate_uniqueness_of(:title) }
  end
end

My most commonly used Shoulda Matchers are:

describe 'Associations' do
  it { should belong_to(:user) }
  it { should have_and_belong_to_many(:collections) }
  it { should have_many(:comments)
end

describe 'Validations' do
  it { should validate_presence_of(:title) }
  it { should validate_uniqueness_of(:title) }
  it { should validate_numericality_of(:phone_number) }
  it { should allow_value('', nil, 1).for(:phone_number) } # if we allow blank in model
  it { should_not allow_value(-1, 31, 'no').for(:position) } # if we only allow positive integers between 0 and 30
end

You can find a complete list of matchers within the Shoulda Matchers GitHub repo.

Shoulda Matchers also allow for more complex chaining. So if we had:

class Article < ApplicationRecord
  has_one :cover_image, class_name: 'Attachment::Image', inverse_of: :article, dependent: :destroy
end

We could use Shoulda Matchers to write:

RSpec.describe Article, type: :model do
  describe 'Associations' do
    it { should have_one(:cover_image).class_name('Attachment::Image').dependent(:destroy) }
  end
end

Recent posts View all

Ruby

Forcing a Rails database column to be not null

How you can force a table column to always have something in it with Rails

Writing Marketing

We've deleted an article's worth of unhelpful words

We've improved several pages across our site by removing words that add no value, and often detract from the article.