Skip to content

Commit

Permalink
Merge pull request #664 from freerange/fix-regression-when-stubbed-me…
Browse files Browse the repository at this point in the history
…thod-expects-hash-but-receives-action-controller-parameters-object

Fix regression when stubbed method expects Hash but receives ActionController::Parameters object
  • Loading branch information
floehopper authored Jul 24, 2024
2 parents 9f917f8 + 228acc9 commit 6a91439
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 1 deletion.
3 changes: 2 additions & 1 deletion lib/mocha/parameter_matchers/has_entries.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
23 changes: 23 additions & 0 deletions test/acceptance/parameter_matcher_test.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
require File.expand_path('../acceptance_test_helper', __FILE__)

require 'hashlike'

class ParameterMatcherTest < Mocha::TestCase
include AcceptanceTest

Expand All @@ -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()
Expand All @@ -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()
Expand Down
11 changes: 11 additions & 0 deletions test/hashlike.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require 'forwardable'

class Hashlike
extend Forwardable

def_delegators :@hash, :keys, :[]

def initialize(hash = {})
@hash = hash
end
end
35 changes: 35 additions & 0 deletions test/unit/parameter_matchers/has_entries_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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])
Expand All @@ -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
Expand Down

0 comments on commit 6a91439

Please sign in to comment.