Skip to content

Commit

Permalink
Support multiple methods in responds_with matcher
Browse files Browse the repository at this point in the history
This provides a terser syntax for matching an object which responds in a
certain way, e.g.

    object = mock()
    object.expects(:method_1).with(responds_with(:upcase => "FOO", :reverse => "oof"))
    object.method_1("foo")
    # no error raised, because "foo".upcase == "FOO" and "foo".reverse == "oof"

Addresses #578.
  • Loading branch information
floehopper committed Nov 12, 2023
1 parent da67bb0 commit f086b7e
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 5 deletions.
37 changes: 32 additions & 5 deletions lib/mocha/parameter_matchers/responds_with.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
require 'mocha/parameter_matchers/base'
require 'mocha/parameter_matchers/all_of'
require 'yaml'

module Mocha
module ParameterMatchers
# Matches any object that responds to +message+ with +result+. To put it another way, it tests the quack, not the duck.
# @overload def responds_with(message, result)
# Matches any object that responds to +message+ with +result+. To put it another way, it tests the quack, not the duck.
# @param [Symbol] message method to invoke.
# @param [Object] result expected result of sending +message+.
# @overload def responds_with(messages_vs_results)
# Matches any object that responds to all the messages with the corresponding results as specified by +messages_vs_results+.
# @param [Hash<Symbol,Object>] messages_vs_results +Hash+ of messages vs results.
# @raise [ArgumentError] if +messages_vs_results+ does not contain at least one entry.
#
# @param [Symbol] message method to invoke.
# @param [Object] result expected result of sending +message+.
# @return [RespondsWith] parameter matcher.
#
# @see Expectation#with
Expand All @@ -22,8 +28,29 @@ module ParameterMatchers
# object.expects(:method_1).with(responds_with(:upcase, "BAR"))
# object.method_1("foo")
# # error raised, because "foo".upcase != "BAR"
def responds_with(message, result)
RespondsWith.new(message, result)
#
# @example Actual parameter responds with "FOO" when :upcase is invoked and "oof" when :reverse is invoked.
# object = mock()
# object.expects(:method_1).with(responds_with(:upcase => "FOO", :reverse => "oof"))
# object.method_1("foo")
# # no error raised, because "foo".upcase == "FOO" and "foo".reverse == "oof"
def responds_with(*options)
case options.length
when 0
raise ArgumentError, 'No arguments. Expecting at least one.'
when 1
option = options.first
raise ArgumentError, 'Argument is not a Hash.' unless option.is_a?(Hash)
raise ArgumentError, 'Argument has no entries.' if option.empty?

matchers = option.map { |message, result| RespondsWith.new(message, result) }
AllOf.new(*matchers)
when 2
message, result = options
RespondsWith.new(message, result)
else
raise ArgumentError, 'Too many arguments; use either a single argument (must be a Hash) or two arguments (a message and a result).'
end
end

# Parameter matcher which matches if actual parameter returns expected result when specified method is invoked.
Expand Down
10 changes: 10 additions & 0 deletions test/unit/parameter_matchers/responds_with_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ def test_should_describe_matcher
matcher = responds_with(:foo, :bar)
assert_equal 'responds_with(:foo, :bar)', matcher.mocha_inspect
end

def test_should_match_parameter_responding_with_expected_values_for_given_messages
matcher = responds_with(upcase: 'FOO', reverse: 'oof')
assert matcher.matches?(['foo'])
end

def test_should_describe_matcher_with_multiple_messages_vs_results
matcher = responds_with(foo: :bar, baz: 123)
assert_equal 'all_of(responds_with(:foo, :bar), responds_with(:baz, 123))', matcher.mocha_inspect
end
end

0 comments on commit f086b7e

Please sign in to comment.