diff --git a/lib/rspec/support/differ.rb b/lib/rspec/support/differ.rb index 9488296e..e63b6350 100644 --- a/lib/rspec/support/differ.rb +++ b/lib/rspec/support/differ.rb @@ -18,6 +18,8 @@ def diff(actual, expected) if any_multiline_strings?(actual, expected) diff = diff_as_string(coerce_to_string(actual), coerce_to_string(expected)) end + elsif all_hashes?(actual, expected) + diff = diff_hashes_as_object(actual, expected) elsif no_procs?(actual, expected) && no_numbers?(actual, expected) diff = diff_as_object(actual, expected) end @@ -56,6 +58,25 @@ def diff_as_string(actual, expected) end # rubocop:enable Metrics/MethodLength + if defined?(RSpec::Mocks::ArgumentMatchers::AnyArgMatcher) + def diff_hashes_as_object(actual, expected) + actual_to_diff = + actual.keys.reduce({}) do |hash, key| + if RSpec::Mocks::ArgumentMatchers::AnyArgMatcher === expected[key] + hash[key] = expected[key] + else + hash[key] = actual[key] + end + hash + end + diff_as_object(actual_to_diff, expected) + end + else + def diff_hashes_as_object(actual, expected) + diff_as_object(actual, expected) + end + end + def diff_as_object(actual, expected) actual_as_string = object_to_string(actual) expected_as_string = object_to_string(expected) @@ -77,6 +98,10 @@ def no_procs?(*args) safely_flatten(args).none? { |a| Proc === a } end + def all_hashes?(actual, expected) + (Hash === actual) && (Hash === expected) + end + def all_strings?(*args) safely_flatten(args).all? { |a| String === a } end diff --git a/spec/rspec/support/differ_spec.rb b/spec/rspec/support/differ_spec.rb index cbd1ed28..1a745006 100644 --- a/spec/rspec/support/differ_spec.rb +++ b/spec/rspec/support/differ_spec.rb @@ -6,7 +6,7 @@ module RSpec module Support - RSpec.describe Differ do + RSpec.describe "Differ" do include Spec::DiffHelpers describe '#diff' do @@ -555,6 +555,31 @@ def inspect; ""; end expect(differ.diff(false, true)).to_not be_empty end end + + describe "fuzzy matcher anything" do + it "outputs only key value pair that triggered diff, anything_key should absorb actual value" do + actual = { :fixed => "fixed", :trigger => "trigger", :anything_key => "bcdd0399-1cfe-4de1-a481-ca6b17d41ed8" } + expected = { :fixed => "fixed", :trigger => "wrong", :anything_key => anything } + diff = differ.diff(actual, expected) + expected_diff = dedent(<<-'EOD') + | + |@@ -1,4 +1,4 @@ + | :anything_key => anything, + | :fixed => "fixed", + |-:trigger => "wrong", + |+:trigger => "trigger", + | + EOD + expect(diff).to be_diffed_as(expected_diff) + end + + it "checks the 'expected' var continues having the 'anything' fuzzy matcher, it has not mutated" do + actual = { :fixed => "fixed", :trigger => "trigger", :anything_key => "bcdd0399-1cfe-4de1-a481-ca6b17d41ed8" } + expected = { :fixed => "fixed", :trigger => "wrong", :anything_key => anything } + differ.diff(actual, expected) + expect(expected).to eq({ :fixed => "fixed", :trigger => "wrong", :anything_key => anything }) + end + end end end end