sajad torkamani

Example use case

Suppose you’re writing a feature test and want to assert that a given route requires authentication. More specifically, you want to test that on visiting a protected route, two things happen:

  • The current path is the login path.
  • An error message is displayed.

You can do this using the default Capybara matchers:

RSpec.describe 'Create quote', type: :feature do
  it 'User must be signed in to access create quote page' do
    visit new_quote_path

    expect(page).to have_current_path new_user_session_path
    expect(page).to have_text unauthenticated_message
  end
end

That doesn’t read bad but this is a test that’ll probably be repeated several times so let’s try and make it more succint and expressive:

RSpec.describe 'Create quote', type: :feature do
  it 'User must be signed in to access create quote page' do
    expect(new_quote_path).to require_authentication
  end
end

The custom matcher here is require_authentication.

How to define the custom matcher

Create a file spec/support/custom_matchers.rb, make sure this file from your rails_helper.rb, and set its contents to:

RSpec::Matchers.define :require_authentication do
  match do |path|
    visit path

    page.has_current_path?(new_user_session_path) &&
      page.has_text?(unauthenticated_message)
  end

  failure_message do |path|
    <<~MSG
      expected that visiting #{path} would redirect the user to#{' '}
      #{new_user_session_path} and show the message \"#{unauthenticated_message}\"
    MSG
  end
end

This matcher can also be rewritten to take a block instead. See this post.

Sources