diff --git a/lib/mocha/parameter_matchers/has_entries.rb b/lib/mocha/parameter_matchers/has_entries.rb index 51bb160b..917ea712 100644 --- a/lib/mocha/parameter_matchers/has_entries.rb +++ b/lib/mocha/parameter_matchers/has_entries.rb @@ -39,7 +39,8 @@ def initialize(entries, exact: false) def matches?(available_parameters) parameter = available_parameters.shift return false unless parameter - return false if @exact && @entries.length != parameter.length + return false unless parameter.respond_to?(:keys) + return false if @exact && @entries.length != parameter.keys.length has_entry_matchers = @entries.map { |key, value| HasEntry.new(key, value) } AllOf.new(*has_entry_matchers).matches?([parameter]) diff --git a/test/acceptance/parameter_matcher_test.rb b/test/acceptance/parameter_matcher_test.rb index 89b936d6..88530151 100644 --- a/test/acceptance/parameter_matcher_test.rb +++ b/test/acceptance/parameter_matcher_test.rb @@ -1,5 +1,7 @@ require File.expand_path('../acceptance_test_helper', __FILE__) +require 'hashlike' + class ParameterMatcherTest < Mocha::TestCase include AcceptanceTest @@ -20,6 +22,16 @@ def test_should_match_hash_parameter_which_is_exactly_the_same assert_passed(test_result) end + def test_should_match_hash_parameter_when_method_invoked_with_hashlike_object_with_no_length_method + test_result = run_as_test do + mock = mock() + hash = { key_1: 'value_1' } + mock.expects(:method).with(hash) + mock.method(Hashlike.new(key_1: 'value_1')) + end + assert_passed(test_result) + end + def test_should_not_match_hash_parameter_which_is_not_exactly_the_same test_result = run_as_test do mock = mock() @@ -38,6 +50,17 @@ def test_should_not_match_hash_parameter_when_method_invoked_with_no_parameters assert_failed(test_result) end + def test_should_not_match_hash_parameter_when_method_invoked_with_empty_hashlike_object_with_no_length_method + test_result = run_as_test do + mock = mock() + hash = { key_1: 'value_1' } + mock.expects(:method).with(hash) + hashlike = Hashlike.new({}) + mock.method(hashlike) + end + assert_failed(test_result) + end + def test_should_match_hash_parameter_with_specified_key test_result = run_as_test do mock = mock() diff --git a/test/hashlike.rb b/test/hashlike.rb new file mode 100644 index 00000000..0794d9c5 --- /dev/null +++ b/test/hashlike.rb @@ -0,0 +1,11 @@ +require 'forwardable' + +class Hashlike + extend Forwardable + + def_delegators :@hash, :keys, :[] + + def initialize(hash = {}) + @hash = hash + end +end diff --git a/test/unit/parameter_matchers/has_entries_test.rb b/test/unit/parameter_matchers/has_entries_test.rb index d165e5c1..e462517e 100644 --- a/test/unit/parameter_matchers/has_entries_test.rb +++ b/test/unit/parameter_matchers/has_entries_test.rb @@ -3,6 +3,7 @@ require 'mocha/parameter_matchers/has_entries' require 'mocha/parameter_matchers/instance_methods' require 'mocha/inspect' +require 'hashlike' class HasEntriesTest < Mocha::TestCase include Mocha::ParameterMatchers @@ -17,6 +18,16 @@ def test_should_not_match_hash_not_including_specified_entries assert !matcher.matches?([{ key_1: 'value_1', key_2: 'value_2' }]) end + def test_should_match_hash_with_the_exact_specified_entries + matcher = HasEntries.new({ key_1: 'value_1', key_2: 'value_2' }, exact: true) + assert matcher.matches?([{ key_1: 'value_1', key_2: 'value_2' }]) + end + + def test_should_not_match_hash_with_the_exact_specified_entries + matcher = HasEntries.new({ key_1: 'value_1', key_2: 'value_2' }, exact: true) + assert !matcher.matches?([{ key_1: 'value_1', key_2: 'value_2', key_3: 'value_3' }]) + end + def test_should_not_match_no_arguments_when_exact_match_not_required matcher = has_entries(key_1: 'value_1') assert !matcher.matches?([nil]) @@ -27,6 +38,30 @@ def test_should_not_match_no_arguments_when_exact_match_required assert !matcher.matches?([nil]) end + def test_should_match_hashlike_object + matcher = has_entries(key_1: 'value_1') + hashlike = Hashlike.new(key_1: 'value_1', key_2: 'value_2') + assert matcher.matches?([hashlike]) + end + + def test_should_not_match_hashlike_object + matcher = has_entries(key_1: 'value_1') + hashlike = Hashlike.new({}) + assert !matcher.matches?([hashlike]) + end + + def test_should_match_hashlike_object_with_no_length_method_when_exact_match_required + matcher = HasEntries.new({ key_1: 'value_1' }, exact: true) + hashlike = Hashlike.new(key_1: 'value_1') + assert matcher.matches?([hashlike]) + end + + def test_should_not_match_hashlike_object_with_no_length_method_when_exact_match_required + matcher = HasEntries.new({ key_1: 'value_1' }, exact: true) + hashlike = Hashlike.new(key_1: 'value_1', key_2: 'value_2') + assert !matcher.matches?([hashlike]) + end + def test_should_describe_matcher matcher = has_entries(key_1: 'value_1', key_2: 'value_2') description = matcher.mocha_inspect