Skip to content

Instantly share code, notes, and snippets.

@benoittgt
Created July 14, 2020 20:42
Show Gist options
  • Save benoittgt/9d7c33aca1f9d96e9804bb74aa02dba4 to your computer and use it in GitHub Desktop.
Save benoittgt/9d7c33aca1f9d96e9804bb74aa02dba4 to your computer and use it in GitHub Desktop.
RSpec matcher with block execution
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rspec"
end
require 'rspec/autorun'
RSpec.configure do |rspec|
rspec.default_formatter = 'doc'
end
# https://twitter.com/kennethmayer/status/1283084667843776515
# Rspec Twitter: Is it possible to write a custom matcher that also yields to a given block?
RSpec.describe do
RSpec::Matchers.define :match_to_string do |expected|
match do |actual|
match = true
unless actual.to_s == expected.to_s
@failure_message_expectation = "expected that #{actual}.to_s would be a eq to #{expected}.to_s. "
match = false
end
unless @matcher_block.call(actual, expected)
@failure_message = @failure_message_custom_matcher
match = false
end
match
end
chain :and_call_custom_matcher do |custom_matcher|
@failure_message_custom_matcher = custom_matcher.failure_message
@matcher_block = custom_matcher.quick_matcher
end
failure_message do |actual|
@failure_message_expectation.to_s + @failure_message.to_s
end
end
Matcher = Struct.new(:quick_matcher, :failure_message)
quick_matcher = ->(first_expect, second_expect) do
first_expect.class == second_expect.class
end
matcher = Matcher.new(quick_matcher, "Class do not match")
it 'no fail' do
expect('u').to match_to_string('u').and_call_custom_matcher(matcher)
end
it 'with first failing matcher' do
expect('u').to match_to_string('v').and_call_custom_matcher(matcher)
end
it 'with second failing matcher' do
expect('u').to match_to_string(:u).and_call_custom_matcher(matcher)
end
it 'with both failing matchers' do
expect('u').to match_to_string(:r).and_call_custom_matcher(matcher)
end
end
@benoittgt
Copy link
Author

Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Using bundler 2.1.4
Using diff-lcs 1.4.4
Using rspec-support 3.9.3
Using rspec-core 3.9.2
Using rspec-expectations 3.9.2
Using rspec-mocks 3.9.1
Using rspec 3.9.0


  no fail
  with first failing matcher (FAILED - 1)
  with second failing matcher (FAILED - 2)
  with both failing matchers (FAILED - 3)

Failures:

  1) with first failing matcher
     Failure/Error: expect('u').to match_to_string('v').and_call_custom_matcher(matcher)
       expected that u.to_s would be a eq to v.to_s.
     # custom_matcher_yield.rb:61:in `block (2 levels) in <main>'

  2) with second failing matcher
     Failure/Error: expect('u').to match_to_string(:u).and_call_custom_matcher(matcher)
       Class do not match
     # custom_matcher_yield.rb:65:in `block (2 levels) in <main>'

  3) with both failing matchers
     Failure/Error: expect('u').to match_to_string(:r).and_call_custom_matcher(matcher)
       expected that u.to_s would be a eq to r.to_s. Class do not match
     # custom_matcher_yield.rb:69:in `block (2 levels) in <main>'

Finished in 0.01511 seconds (files took 0.07976 seconds to load)
4 examples, 3 failures

Failed examples:

rspec custom_matcher_yield.rb:60 # with first failing matcher
rspec custom_matcher_yield.rb:64 # with second failing matcher
rspec custom_matcher_yield.rb:68 # with both failing matchers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment