-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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