Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HasKeys parameter matcher #512

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mocha/parameter_matchers.rb
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
@@ -0,0 +1,53 @@
require 'mocha/parameter_matchers/base'

module Mocha
module ParameterMatchers
# Matches +Hash+ containing +keys+.
#
# @param [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 entry with 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+ entry with expected keys.
class HasKeys < Base
# @private
def initialize(*keys)
raise ArgumentError, 'Argument has no keys.' 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[1...-1]})"
end
end
end
end
64 changes: 64 additions & 0 deletions test/unit/parameter_matchers/has_keys_test.rb
@@ -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 'Argument has no keys.', e.message
end
end