Introduction

Shoulda Matchers provides Test::Unit and RSpec-compatible one-liners that test common Rails functionality. It helps you write tests that would otherwise be much longer, more complex, and error-prone.

Using shoulda-matchers for testing simplifies the entire process.

Let’s take a look at shoulda-matchers in action, compared to its RSpec-only equivalent.

One-Liner ActiveModel Tests

Below is an RSpec unit test for an ActiveModel. Each named test goes through four distinct phases: setup, exercise, verify, and teardown. The first named test ensures that you can’t create a valid Contact record without a first_name attribute; the second test makes sure each Contact record has a unique email address, and the third enforces a minimum password length requirement.

describe Contact do
  it "is invalid without a firstname" do
    contact = Contact.create(first_name: nil, email: 'ted@example.com', password: 'empirestatebuilding')

    contact.valid?

    expect(contact.errors[:firstname]).to include("can't be blank")
  end

  it "is invalid with a duplicate email address" do
    Contact.create(first_name: 'Ted', email: 'ted@example.com', password: 'empirestatebuilding')
    contact = Contact.new(first_name: 'Robin', email: 'ted@example.com', password: 'montrealcanadiens')

    contact.valid?

    expect(contact.errors[:emal]).to include("has already been taken")
  end

  it "is invalid without a minimum password length" do
    contact_one = Contact.create(first_name: 'Ted', email: 'ted@example.com', password: 'empire')

    contact_one.valid?

    expect(Contact.errors[:password]).to include("password is too short")
  end
end

At every phase of the RSpec-only test cases, you need to recall or reference too much information to correctly implement each one. You need to remember that Rails stores error messages as a Hash, and you also need to remember the exact text for each error message.

That alone can discourage you from testing your Rails app because it slows you down.

In this example, shoulda-matchers reduces about 20 lines of test code to just three, but gives you the same assurance that you can change your code at any time.

describe Contact do
  it { is_expected.to validate_presence_of(:firstname) }
  it { is_expected.to validate_uniqueness_of(:email) }
  it { is_expected.to validate_length_of(:password).is_at_least(10) }
end

One-Liner ActionController Tests

In Rails, it’s essential for each controller action to have tests that assert the core HTTP response properties.

For a basic Rails controller, you’d test the following:

  • What was the response status code?
  • Did the controller render the expected template?
  • Did the controller render the expected Rails layout?
  • Did the controller set any flash messages?
  • Was any information inserted or deleted from the session?
  • Did the controller redirect the user to a new URL?

Here’s what such a controller test might look like using shoulda-matchers:

describe ContactsController do
  describe 'GET #index' do
    context 'when user is logged in' do
      with :user
      before do
        sign_in user
        get :index
      end
      it { is_expected.to respond_with :ok }
      it { is_expected.to render_with_layout :application }
      it { is_expected.to render_template :index }
    end
    context 'when user is logged out' do
      before do
        get :index
      end
      it { is_expected.to redirect_to new_session_path }
      it { is_expected.to set_the_flash(:warning).to('Please log in.') }
      it { is_expected.to set_session(:return_to).to(contacts_path) }
    end
  end
end

Controllers are rarely tested in Rails apps because in behavior-driven development a feature spec usually covers the work of multiple controller specs.

Still, shoulda-matchers makes it a lot easier to test a controller method.

One-Liner ActiveRecord Tests

Since ActiveRecord associations are well-tested by the Rails test suite, most people just trust that they’ll work, and don’t feel the need to test them.

However, if you want to make sure that your models are using any associations you’ve declared, shoulda-matchers can help you accomplish that easily.

Here’s how you’d write a has_many association for a Contact record, assuming it has many phone numbers using just RSpec:

describe Contact do
  it "can have many phone numbers" do
    contact = Contact.reflect_on_association(:phones)
    expect(contact.macro).to eql(:has_many)
  end
end

The shoulda-matchers version of the has_many association is easier to read, and clearly states the intent of your test. Adding a qualifier is just a matter of chaining the methods available to you.

describe Contact do
  it { is_expected.to have_many(:phones).dependent(:destroy) }
end

As shown in the examples above, shoulda-matchers reduces the amount of test code you need to write for your Rails application — you can write robust tests that aren’t lengthy or complicated. The official documentation for shoulda-matchers shows you more one-liners, along with information about how to use them.

P.S. Would you like to learn how to build sustainable Rails apps and ship more often? We’ve recently published an ebook covering just that — “Rails Testing Handbook”. Learn more and download a free copy.