Skip to content

Commit

Permalink
Add ParameterMatchers#has_keys
Browse files Browse the repository at this point in the history
c.f. ParameterMatchers#has_entries
  • Loading branch information
cstyles authored and floehopper committed Jun 20, 2021
1 parent d8f44bc commit 18d8104
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/mocha/parameter_matchers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module ParameterMatchers; end
require 'mocha/parameter_matchers/has_entry'
require 'mocha/parameter_matchers/has_entries'
require 'mocha/parameter_matchers/has_key'
require 'mocha/parameter_matchers/has_keys'
require 'mocha/parameter_matchers/has_value'
require 'mocha/parameter_matchers/includes'
require 'mocha/parameter_matchers/instance_of'
Expand Down
53 changes: 53 additions & 0 deletions lib/mocha/parameter_matchers/has_keys.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'mocha/parameter_matchers/base'

module Mocha
module ParameterMatchers
# Matches +Hash+ containing +keys+.
#
# @param [*Array<Object>] keys expected keys.
# @return [HasKeys] parameter matcher.
#
# @see Expectation#with
#
# @example Actual parameter contains entry with expected keys.
# object = mock()
# object.expects(:method_1).with(has_keys(:key_1, :key_2))
# object.method_1(:key_1 => 1, :key_2 => 2, :key_3 => 3)
# # no error raised
#
# @example Actual parameter does not contain all expected keys.
# object = mock()
# object.expects(:method_1).with(has_keys(:key_1, :key_2))
# object.method_1(:key_2 => 2)
# # error raised, because method_1 was not called with Hash containing key: :key_1
#
def has_keys(*keys) # rubocop:disable Naming/PredicateName
HasKeys.new(*keys)
end

# Parameter matcher which matches when actual parameter contains +Hash+ with all expected keys.
class HasKeys < Base
# @private
def initialize(*keys)
raise ArgumentError, 'No arguments. Expecting at least one.' if keys.empty?

@keys = keys
end

# @private
def matches?(available_parameters)
parameter = available_parameters.shift
return false unless parameter.respond_to?(:keys)

@keys.map(&:to_matcher).all? do |matcher|
parameter.keys.any? { |key| matcher.matches?([key]) }
end
end

# @private
def mocha_inspect
"has_keys(#{@keys.mocha_inspect(false)})"
end
end
end
end
64 changes: 64 additions & 0 deletions test/unit/parameter_matchers/has_keys_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
require File.expand_path('../../../test_helper', __FILE__)

require 'mocha/parameter_matchers/has_keys'
require 'mocha/parameter_matchers/instance_methods'
require 'mocha/inspect'

class HasKeysTest < Mocha::TestCase
include Mocha::ParameterMatchers

def test_should_match_hash_including_specified_keys
matcher = has_keys(:key_1, :key_2)
assert matcher.matches?([{ :key_1 => 1, :key_2 => 2, :key_3 => 3 }])
end

def test_should_not_match_hash_not_including_specified_keys
matcher = has_keys(:key_1, :key_2)
assert !matcher.matches?([{ :key_3 => 3 }])
end

def test_should_not_match_hash_not_including_all_keys
matcher = has_keys(:key_1, :key_2)
assert !matcher.matches?([{ :key_1 => 1, :key_3 => 3 }])
end

def test_should_describe_matcher
matcher = has_keys(:key_1, :key_2)
assert_equal 'has_keys(:key_1, :key_2)', matcher.mocha_inspect
end

def test_should_match_hash_including_specified_key_with_nested_key_matcher
matcher = has_keys(equals(:key_1), equals(:key_2))
assert matcher.matches?([{ :key_1 => 1, :key_2 => 2 }])
end

def test_should_not_match_hash_not_including_specified_keys_with_nested_key_matchers
matcher = has_keys(equals(:key_1), equals(:key2))
assert !matcher.matches?([{ :key_2 => 2 }])
end

def test_should_not_raise_error_on_empty_arguments
matcher = has_keys(:key_1, :key_2)
assert_nothing_raised { matcher.matches?([]) }
end

def test_should_not_match_on_empty_arguments
matcher = has_keys(:key_1, :key_2)
assert !matcher.matches?([])
end

def test_should_not_raise_error_on_argument_that_does_not_respond_to_keys
matcher = has_keys(:key_1, :key_2)
assert_nothing_raised { matcher.matches?([:key_1]) }
end

def test_should_not_match_on_argument_that_does_not_respond_to_keys
matcher = has_keys(:key_1, :key_2)
assert !matcher.matches?([:key_1])
end

def test_should_raise_argument_error_if_no_keys_are_supplied
e = assert_raises(ArgumentError) { has_keys }
assert_equal 'No arguments. Expecting at least one.', e.message
end
end

0 comments on commit 18d8104

Please sign in to comment.