How to write custom RSpec matchers in your Ruby on Rails app
Custom RSpec matchers are powerful tools that can significantly improve the readability and maintainability of your test suite. In this article, we'll explore how to create and implement custom matchers that make your specs more expressive and easier to understand.
Understanding the need for custom matchers
Let's start with a common scenario. Suppose you're testing a background color property:
it "has valid color" do
background = 'green'
expect(background).to eq('green')
end
While this works, we can make it more readable and reusable with custom matches. Please remember this is just an example; I know it's overkill here.
Creating your first custom matcher
Let's create a specialized matcher for checking if something is green:
# spec/support/matchers/be_green.rb
RSpec::Matchers.define :be_green do
match do |actual|
actual == 'green'
end
failure_message do |actual|
"expected #{actual.inspect} to be 'green'"
end
failure_message_when_negated do |actual|
"expected #{actual.inspect} not to be 'green'"
end
description do
"be 'green'"
end
end
Building flexible custom matchers
Sometimes we need more flexibility. Here's a more versatile color matcher:
# spec/support/matchers/has_color.rb
RSpec::Matchers.define :has_color do |expected|
match do |actual|
actual == expected
end
failure_message do |actual|
"expected #{actual.inspect} to be '#{expected}'"
end
failure_message_when_negated do |actual|
"expected #{actual.inspect} not to be '#{expected}'"
end
description do
"has color '#{expected}'"
end
end
Implementing custom matchers in tests
Here's how to use these matchers in your specs:
require_relative '../support/matchers/be_green'
require_relative '../support/matchers/has_color'
# or include these above in spec_helper.rb
RSpec.describe 'Color Tests' do
let(:background) { 'green' }
it 'has valid color' do
expect(background).to be_green
end
it 'has valid color using our custom matcher' do
expect(background).to has_color('green')
end
it 'return fail with error msg' do
expect(background).to has_color('red')
end
end
Summary
Custom RSpec matchers enhance test readability and maintainability by providing domain-specific language for your tests. They help create more expressive and cleaner test suites while maintaining clear failure messages. It's very useful.
Happy custom matching!