Custom Matchers

Paul Götze edited this page Aug 31, 2015 · 12 revisions

Writing custom matchers is dead simple now, with the new Matcher DSL released in rspec-1.2.0. We’ve made a couple of small changes, since its initial release, so what follows works with rspec-1.2.4 and up. Notes about previous versions below.

Start with an example you want to refactor

Let’s say you’ve got a code example like this:

describe 9 do
  it "should be a multiple of 3" do
    (9 % 3).should == 0
  end
end

What we really want to say here is that 9 should be a multiple of 3, like this:

describe 9 do
  it "should be a multiple of 3" do
    9.should be_a_multiple_of(3)
  end
end

Writing the matcher

Here’s how to write the matcher for this:

RSpec::Matchers.define :be_a_multiple_of do |expected|
  match do |actual|
    actual % expected == 0
  end
end

That’s it!

How it works

The define() method defines a method named be_a_multiple_of(expected) that returns matcher object initialized with the expected value. So be_a_multiple_of(3) assigns 3 as the expected value.

The match method within the matcher definition block defines a match block. If called with the should() method, like so:

9.should be_a_multiple_of(3)

… then the expectation will pass if the block returns true. If the block returns false, the example will fail with an error message saying “expected 9 to be a multiple of 3”

If called with should_not(), like so:

9.should_not be_a_multiple_of(3)

… then the expectation will pass if the block returns false. If the block returns true, in this case, then the example will fail with an error message saying “expected 9 not to be a multiple of 3”

Auto-generated description/docstring

If you’re fond of writing your code examples like this:

describe 9 do
  it { should be_a_multiple_of(3) }
end

… you can do that, and the matcher will generate output like this:

9
- should be a multiple of 3

Custom failure messages

If you prefer to customize the failure messages, you can do so like this:

RSpec::Matchers.define :be_a_multiple_of do |expected|
  match do |actual|
    actual % expected == 0
  end

  failure_message_for_should do |actual|
    "expected that #{actual} would be a precise multiple of #{expected}"
  end

  failure_message_for_should_not do |actual|
    "expected that #{actual} would not be a precise multiple of #{expected}"
  end

  description do
    "be a precise multiple of #{expected}"
  end
end

Helper Methods

Not much to say here: just write them:

RSpec::Matchers.define :be_a_multiple_of do |expected|
  match do |actual|
    do_the_math(actual, expected)
  end

  def do_the_math(actual, expected)
    actual % expected == 0
  end
end

Diffable

Coming soon …

Where to store them

You can store custom matcher definitions anywhere you want, but the convention is to keep them in files spec/support/matchers/ and then require them from spec/spec_helper.rb like this:

Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each { |f| require f }

Limitations

Coming soon …

Changes since rspec-1.2.0

rspec-1.2.3 deprecates Spec::Matchers.create in favor of Spec::Matchers.define. The deprecated method will be removed from rspec-1.3. rspec-2 deprecates Spec::Matchers.define in favor of RSpec::Matchers.define.

rspec-1.2.3 introduced the diffable() method, so previous versions don’t support it.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.