Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I alias RSpec matchers?

Tags:

ruby

rspec

I've got these two matchers that are identical apart from their names:

RSpec::Matchers.define :be_the_downcased_version_of do |actual|
    match do |actual|
        @actual = actual
        @expected = expected

        @actual == @expected.downcase
    end

    failure_message do |actual|
        "Expected #{@actual.inspect} to be the downcased version of #{expected.inspect}.\nIt was not: #{@actual.inspect}"
    end

    failure_message_when_negated do |actual|
        "Expected #{@actual.inspect} to not be the downcased version of #{expected.inspect}.\nIt was: #{@actual.inspect}"
    end

    description do
        "be the downcased version of #{expected.inspect}"
    end
end

RSpec::Matchers.define :return_the_downcased_version_of do |actual|
    match do |actual|
        @actual = actual
        @expected = expected

        @actual == @expected.downcase
    end

    failure_message do |actual|
        "Expected #{@actual.inspect} to be the downcased version of #{expected.inspect}.\nIt was not: #{@actual.inspect}"
    end

    failure_message_when_negated do |actual|
        "Expected #{@actual.inspect} to not be the downcased version of #{expected.inspect}.\nIt was: #{@actual.inspect}"
    end

    description do
        "be the downcased version of #{expected.inspect}"
    end
end

Can I alias them like when aliasing a method with Ruby?

alias_method :foo_meth, :bar_meth
like image 908
Starkers Avatar asked Aug 20 '14 12:08

Starkers


2 Answers

As cbliard commented, you can simply do

RSpec::Matchers.define :be_the_downcased_version_of do |expected|
  match do |actual|
    # ...
  end
  # ...
end

RSpec::Matchers.alias_matcher :return_the_downcased_version_of, :be_the_downcased_version_of

to alias the matcher. Which is in my opinion a very clean solution.

like image 177
Rouven B. Avatar answered Nov 12 '22 06:11

Rouven B.


Yes, you can alias a matcher defined with RSpec's matcher DSL. The most convenient way that I know to do it is to reopen the example context's class in a before block and alias the method there.

But first, let's clean up your matcher.

  1. The block passed to Matchers.define takes the expected value, not the actual value, as its argument.
  2. You don't need those instance variables; their values are already available in block arguments.

In spec/support/be_the_downcased_version_of.rb:

RSpec::Matchers.define :be_the_downcased_version_of do |expected|
  match do |actual|
    actual == expected.downcase
  end

  failure_message_for_should do |actual|
    "Expected #{actual.inspect} to be the downcased version of #{expected.inspect}.\nIt was not: #{actual.inspect}"
  end

  failure_message_for_should_not do |actual|
    "Expected #{actual.inspect} to not be the downcased version of #{expected.inspect}.\nIt was: #{actual.inspect}"
  end

  description do
    "be the downcased version of #{expected.inspect}"
  end

end

To alias the method defined by that matcher, in spec/spec_helper.rb:

config.before :each do
  class << self
    alias_method :return_the_downcased_version_of, :be_the_downcased_version_of
  end
end

Here's an alternate approach. I wouldn't actually do this, because it requires more boilerplate, but it uses less magic than the previous approach so it might be informative. Just implement the matcher without the DSL and alias the method normally. In spec/support/be_the_downcased_version_of.rb:

alias_method :return_the_downcased_version_of, :be_the_downcased_version_of

def be_the_downcased_version_of(actual)
  BeTheDowncasedVersionOf.new actual
end

class BeTheDowncasedVersionOf
  def initialize(expected)
    @expected = expected
  end

  def matches?(actual)
    @actual = actual
    @actual == @expected.downcase
  end

  def failure_message_for_should
    "Expected #{@actual.inspect} to be the downcased version of #{@expected.inspect}.\nIt was not: #{@actual.inspect}"
  end

  def failure_message_for_should_not
    "Expected #{@actual.inspect} to not be the downcased version of #{@expected.inspect}.\nIt was: #{@actual.inspect}"
  end

  def description
    "be the downcased version of #{@expected.inspect}"
  end

end
like image 38
Dave Schweisguth Avatar answered Nov 12 '22 06:11

Dave Schweisguth