Skip to content

Instantly share code, notes, and snippets.

@wasabigeek
Created January 6, 2022 15:38
Show Gist options
  • Save wasabigeek/74c048d87ac1f80edfff13cdab9883b0 to your computer and use it in GitHub Desktop.
Save wasabigeek/74c048d87ac1f80edfff13cdab9883b0 to your computer and use it in GitHub Desktop.
Mocha kwarg matching patch
module Mocha
class Mock
# change to *args, **kwargs
def method_missing(symbol, *args, **kwargs, &block)
check_expiry
check_responder_responds_to(symbol)
# change to *arg, **kwargs
invocation = Invocation.new(self, symbol, *args, **kwargs, &block)
if (matching_expectation_allowing_invocation = all_expectations.match_allowing_invocation(invocation))
matching_expectation_allowing_invocation.invoke(invocation)
elsif (matching_expectation = all_expectations.match(invocation)) || (!matching_expectation && !@everything_stubbed)
raise_unexpected_invocation_error(invocation, matching_expectation)
end
end
end
class Invocation
attr_reader :args, :kwargs
# change *arguments to *args, **kwargs
def initialize(mock, method_name, *args, **kwargs, &block)
@args = args
@kwargs = kwargs
@mock = mock
@method_name = method_name
@arguments = [*args, kwargs]
@block = block
@yields = []
@result = nil
end
end
class Expectation
# change *expected_parameters to *expected_args, **expected_kwargs
def with(*expected_args, **expected_kwargs, &matching_block)
@parameters_matcher = ParametersMatcher.new(*expected_args, **expected_kwargs, &matching_block)
self
end
def match?(invocation)
# change invocation.arguments to *invocation.args, **invocation.kwargs
@method_matcher.match?(invocation.method_name) \
&& @parameters_matcher.match?(*invocation.args, **invocation.kwargs) && @block_matcher.match?(invocation.block) && in_correct_order?
end
end
class ParametersMatcher
# change to *expected_args, **expected_kwargs
def initialize(*expected_args, **expected_kwargs, &matching_block)
@expected_args = expected_args.empty? ? [ParameterMatchers::AnyParameters.new] : expected_args
@expected_kwargs = expected_kwargs.empty? ? ParameterMatchers::AnyParameters.new : ParameterMatchers::HasEntries.new(expected_kwargs) # warning: HasEntries is more permissive than checking hash equality
@expected_parameters = [*expected_args, expected_kwargs] # temporary
@matching_block = matching_block
end
# change to *actual_args, **actual_kwargs
def match?(*actual_args, **actual_kwargs)
if @matching_block
@matching_block.call(*actual_args, **actual_kwargs)
else
parameters_match?(*actual_args, **actual_kwargs)
end
end
# change to *actual_args, **actual_kwargs
def parameters_match?(*actual_args, **actual_kwargs)
arg_matchers.all? { |matcher| matcher.matches?(actual_args) } \
&& kwarg_matcher.matches?([actual_kwargs]) \
&& actual_args.empty?
end
def arg_matchers
@expected_args.map(&:to_matcher)
end
def kwarg_matcher
@expected_kwargs
end
end
class StubbedMethod
def define_new_method
self_in_scope = self
method_name_in_scope = method_name
# change *args to *args, **kwargs
stub_method_owner.send(:define_method, method_name) do |*args, **kwargs, &block|
self_in_scope.mock.method_missing(method_name_in_scope, *args, **kwargs, &block)
end
retain_original_visibility(stub_method_owner)
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment