Skip to content

Instantly share code, notes, and snippets.

@gomo
Last active October 4, 2018 04:12
Show Gist options
  • Save gomo/83e021828a0aeade3b3370d404c2ee7b to your computer and use it in GitHub Desktop.
Save gomo/83e021828a0aeade3b3370d404c2ee7b to your computer and use it in GitHub Desktop.
ChainChecker is used when you want to group multiple `expect`.
# frozen_string_literal: true
##
# {ChainChecker} is used when you want to group multiple `expect`.
# You use it including in `RSpec::Matchers.define` block.
#
# RSpec::Matchers.define :have_foo_bar do |foo, bar|
# include ChainChecker
# end
#
# The block passed to the main method becomes the entry point.
#
# RSpec::Matchers.define :have_foo_bar do |foo, bar|
# include ChainChecker
#
# main do |actual|
# expect(actual.foo).to eq foo
# expect(actual.bar).to eq bar
# end
# end
#
# If you use `expect` in the `match` method, the error messages are very inconvenient, but the error that occurs within the `main` method will display user-friendly messages.
# The test code is as follows.
#
# context 'some' do
# it 'should have foo and bar' do
# some = Some.find(1)
# expect(some).to have_foo_bar('foo value', 'bar value')
# end
# end
#
# If you want to do additional tests, you can chain them with chain methods.
# Using the check block in the chain method, you can write the test more naturally.
#
# RSpec::Matchers.define :have_foo_bar do |foo, bar|
# include ChainChecker
#
# chain :with_baz_qux do |baz, qux|
# check do |actual|
# expect(actual.baz).to eq baz
# expect(actual.qux).to eq qux
# end
# end
# end
#
# The test code is as follows.
#
# context 'some value' do
# it 'should have foo and bar' do
# some = Some.find(1)
# expect(some).to have_foo_bar('foo value', 'bar value').with_baz_qux('baz value', 'qux value')
# end
# end
#
module ChainChecker
def self.included(base)
base.define_singleton_method :main do |options = {}, &main_block|
match options do |actual|
instance_exec(actual, &main_block)
execute_chain_checks(actual)
rescue RSpec::Expectations::ExpectationNotMetError => e
failure_position = e.backtrace.find {|path| path.exclude?('/lib/ruby/gems/') }
@failure_message = "#{e.message}\nat #{failure_position}"
false
end
end
base.failure_message do |_|
@failure_message
end
end
def check(&block)
checks << block
end
private
def checks
@checks ||= []
end
def execute_chain_checks(actual)
checks.each do |block|
block.call(actual)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment