Skip to content

Instantly share code, notes, and snippets.

@mltsy
Last active September 23, 2021 23:15
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mltsy/21fd5e15ae12a004c8b13d6ec4534458 to your computer and use it in GitHub Desktop.
Save mltsy/21fd5e15ae12a004c8b13d6ec4534458 to your computer and use it in GitHub Desktop.
RSpec deep include matcher for nested hashes (ignores order and extra keys/values)
RSpec::Matchers.define :deep_include do |expected|
match { |actual| deep_include? actual, expected }
def deep_include?(actual, expected, path = [])
return true if actual == expected
@failing_path = path
@failing_expected = expected
@failing_actual = actual
if actual.is_a? Array
return false unless expected.is_a? Array
expected.each_with_index do |expected_item, index|
match_found = actual.any? do |actual_item|
deep_include? actual_item, expected_item, path + [index]
end
unless match_found
@failing_array = actual
@failing_array_path = path + [index]
@failing_expected_array_item = expected_item
return false
end
end
elsif actual.is_a? Hash
return false unless expected.is_a? Hash
expected.all? do |key, expected_value|
return false unless actual.has_key? key
deep_include? actual[key], expected_value, path + [key]
end
else
false
end
end
failure_message do |actual|
if @failing_array_path
path = @failing_array_path.map{ |p| "[#{p.inspect}]" }.join
path = "root" if path.blank?
message = "Actual array did not include value at #{path}: \n" +
" expected #{@failing_expected_array_item.inspect}\n" +
" but matching value not found in array: #{@failing_array}\n"
else
path = @failing_path.map{ |p| "[#{p.inspect}]" }.join
path = "root" if path.blank?
message = "Actual hash did not include expected value at #{path}: \n" +
" expected #{@failing_expected.inspect}\n" +
" got #{@failing_actual.inspect}\n"
end
message
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment