Skip to content

Instantly share code, notes, and snippets.

@reabiliti
Forked from mltsy/deep_include_matcher.rb
Last active April 21, 2020 15:15
Show Gist options
  • Save reabiliti/530c2e260468f33b149f2abf5937bae2 to your computer and use it in GitHub Desktop.
Save reabiliti/530c2e260468f33b149f2abf5937bae2 to your computer and use it in GitHub Desktop.
RSpec deep include matcher for nested hashes (ignores order and extra keys/values)
# frozen_string_literal: true
RSpec::Matchers.define(:deep_include) do |expected|
# Found there https://gist.github.com/mltsy/21fd5e15ae12a004c8b13d6ec4534458 and added some changes
# For example we have some spec:
# expect(query_result).to deep_include(
# tickets: {
# edges: [
# {
# node: {
# id: kind_of(String)
# }
# },
# ]
# }
# )
#
# for this one query_result:
# #<RecursiveOpenStruct tickets={"edges"=>[{"node"=>{"id"=>"1"}}, {"node"=>{"id"=>"2"}}]}>
#
match do |actual|
object = actual.is_a?(OpenStruct) ? actual.to_h : actual
deep_include?(object, expected)
end
def deep_include?(actual, expected, path = [])
return true if expected === actual
@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
next if match_found
@failing_array = actual
@failing_array_path = path + [index]
@failing_expected_array_item = expected_item
return false
end
elsif actual.is_a?(Hash)
return false unless expected.is_a?(Hash)
expected.all? do |key, expected_value|
return false unless actual.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