Writing custom RSpec expectation matchers is a great way to enhance the readability of your specifications. As an example, here's a simple matcher to check whether a collection contains any of the given items. An example will make things clearer:
# Passing example
odds = [1,3,5,7,9]
evens = [2,4,6,8]
odds.should_not include_any(*evens)
One thing to note is that include_any expects multiple arguments rather than a collection, hence the splat. Here's the code:
module Spec
module Matchers
def include_any(*args)
IncludeAny.new(*args)
end
class IncludeAny
def initialize(*args)
@args = args
end
def matches?(target)
@target = target
@target.include_any?(*@args)
end
def failure_message
"expected #{@target.inspect} to include one or more elements from #{@args.inspect}"
end
def negative_failure_message
"expected #{@target.inspect} not to include any elements from #{@args.inspect}"
end
end
end
end
This is about as trivial as a custom matcher gets. All of the work is done by the include_any? method, which doesn't actually exist as part of the core Ruby API. In fact it's a simple extension to Enumerable that I think I originally borrowed from Merb. Put the following in config/initializers/core_extensions.rb or similar to use it in a Rails app.
module Enumerable
def include_any?(*args)
args.any? { |arg| self.include?(arg) }
end
end
Which custom matchers are you using?