summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
author(no author) <(no author)@980ebf18-57e1-0310-9a29-db15c13687c0>2007-08-09 08:41:54 +0000
committer(no author) <(no author)@980ebf18-57e1-0310-9a29-db15c13687c0>2007-08-09 08:41:54 +0000
commit5a25701723431e0ebe2d7134ab65d56bee2c5244 (patch)
tree248522b40031089a779760d41003fc23e00f916b
parent5e8d71da3e060036c695a2db904c880567fc9f69 (diff)
downloadpuppet-5a25701723431e0ebe2d7134ab65d56bee2c5244.tar.gz
puppet-5a25701723431e0ebe2d7134ab65d56bee2c5244.tar.xz
puppet-5a25701723431e0ebe2d7134ab65d56bee2c5244.zip
Upgrade mocha to 0.5.1, which gives much better error messages
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@2758 980ebf18-57e1-0310-9a29-db15c13687c0
-rw-r--r--test/lib/mocha/auto_verify.rb12
-rw-r--r--test/lib/mocha/deprecation.rb22
-rw-r--r--test/lib/mocha/exception_raiser.rb17
-rw-r--r--test/lib/mocha/expectation.rb251
-rw-r--r--test/lib/mocha/infinite_range.rb10
-rw-r--r--test/lib/mocha/inspect.rb4
-rw-r--r--test/lib/mocha/is_a.rb9
-rw-r--r--test/lib/mocha/missing_expectation.rb27
-rw-r--r--test/lib/mocha/mock.rb197
-rw-r--r--test/lib/mocha/mock_methods.rb122
-rw-r--r--test/lib/mocha/multiple_yields.rb20
-rw-r--r--test/lib/mocha/no_yields.rb11
-rw-r--r--test/lib/mocha/object.rb12
-rw-r--r--test/lib/mocha/parameter_matchers.rb9
-rw-r--r--test/lib/mocha/parameter_matchers/all_of.rb39
-rw-r--r--test/lib/mocha/parameter_matchers/any_of.rb44
-rw-r--r--test/lib/mocha/parameter_matchers/anything.rb30
-rw-r--r--test/lib/mocha/parameter_matchers/has_entry.rb39
-rw-r--r--test/lib/mocha/parameter_matchers/has_key.rb39
-rw-r--r--test/lib/mocha/parameter_matchers/has_value.rb39
-rw-r--r--test/lib/mocha/parameter_matchers/includes.rb37
-rw-r--r--test/lib/mocha/return_values.rb31
-rw-r--r--test/lib/mocha/single_return_value.rb24
-rw-r--r--test/lib/mocha/single_yield.rb18
-rw-r--r--test/lib/mocha/standalone.rb2
-rw-r--r--test/lib/mocha/stub.rb18
-rw-r--r--test/lib/mocha/test_case_adapter.rb6
-rw-r--r--test/lib/mocha/yield_parameters.rb31
28 files changed, 892 insertions, 228 deletions
diff --git a/test/lib/mocha/auto_verify.rb b/test/lib/mocha/auto_verify.rb
index 7aa5e8b30..dce877bde 100644
--- a/test/lib/mocha/auto_verify.rb
+++ b/test/lib/mocha/auto_verify.rb
@@ -1,12 +1,12 @@
require 'mocha/mock'
-# Methods added to TestCase allowing creation of mock objects.
-#
-# Mocks created this way will have their expectations automatically verified at the end of the test.
-#
-# See Mocha::MockMethods for methods on mock objects.
-module Mocha
+module Mocha # :nodoc:
+ # Methods added to TestCase allowing creation of traditional mock objects.
+ #
+ # Mocks created this way will have their expectations automatically verified at the end of the test.
+ #
+ # See Mock for methods on mock objects.
module AutoVerify
def mocks # :nodoc:
diff --git a/test/lib/mocha/deprecation.rb b/test/lib/mocha/deprecation.rb
new file mode 100644
index 000000000..7448510ec
--- /dev/null
+++ b/test/lib/mocha/deprecation.rb
@@ -0,0 +1,22 @@
+module Mocha
+
+ class Deprecation
+
+ class << self
+
+ attr_accessor :mode, :messages
+
+ def warning(message)
+ @messages << message
+ $stderr.puts "Mocha deprecation warning: #{message}" unless mode == :disabled
+ $stderr.puts caller.join("\n ") if mode == :debug
+ end
+
+ end
+
+ self.mode = :enabled
+ self.messages = []
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/exception_raiser.rb b/test/lib/mocha/exception_raiser.rb
new file mode 100644
index 000000000..266e209a2
--- /dev/null
+++ b/test/lib/mocha/exception_raiser.rb
@@ -0,0 +1,17 @@
+module Mocha # :nodoc:
+
+ class ExceptionRaiser # :nodoc:
+
+ def initialize(exception, message)
+ @exception, @message = exception, message
+ end
+
+ def evaluate
+ raise @exception, @exception.to_s if @exception == Interrupt
+ raise @exception, @message if @message
+ raise @exception
+ end
+
+ end
+
+end
diff --git a/test/lib/mocha/expectation.rb b/test/lib/mocha/expectation.rb
index 0073c1e13..49b39bea9 100644
--- a/test/lib/mocha/expectation.rb
+++ b/test/lib/mocha/expectation.rb
@@ -1,21 +1,18 @@
require 'mocha/infinite_range'
require 'mocha/pretty_parameters'
require 'mocha/expectation_error'
+require 'mocha/return_values'
+require 'mocha/exception_raiser'
+require 'mocha/yield_parameters'
+require 'mocha/is_a'
-class Object
-
- alias_method :__is_a__, :is_a?
-
-end
-
-module Mocha
- # Methods on expectations returned from Mocha::MockMethods#expects and Mocha::MockMethods#stubs
+module Mocha # :nodoc:
+
+ # Methods on expectations returned from Mock#expects, Mock#stubs, Object#expects and Object#stubs.
class Expectation
# :stopdoc:
- class InvalidExpectation < Exception; end
-
class AlwaysEqual
def ==(other)
true
@@ -26,23 +23,26 @@ module Mocha
def initialize(mock, method_name, backtrace = nil)
@mock, @method_name = mock, method_name
- @count = 1
+ @expected_count = 1
@parameters, @parameter_block = AlwaysEqual.new, nil
- @invoked, @return_value = 0, nil
+ @invoked_count, @return_values = 0, ReturnValues.new
@backtrace = backtrace || caller
- @yield = nil
+ @yield_parameters = YieldParameters.new
end
- def yield?
- @yield
- end
-
def match?(method_name, *arguments)
+ return false unless @method_name == method_name
if @parameter_block then
- @parameter_block.call(*arguments)
+ return false unless @parameter_block.call(*arguments)
+ else
+ return false unless (@parameters == arguments)
+ end
+ if @expected_count.is_a?(Range) then
+ return false unless @invoked_count < @expected_count.last
else
- (@method_name == method_name) and (@parameters == arguments)
+ return false unless @invoked_count < @expected_count
end
+ return true
end
# :startdoc:
@@ -54,34 +54,63 @@ module Mocha
# +range+ can be specified as an exact integer or as a range of integers
# object = mock()
# object.expects(:expected_method).times(3)
- # 3.times { object.expected_method } # => verify succeeds
+ # 3.times { object.expected_method }
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).times(3)
- # 2.times { object.expected_method } # => verify fails
+ # 2.times { object.expected_method }
+ # # => verify fails
#
# object = mock()
# object.expects(:expected_method).times(2..4)
- # 3.times { object.expected_method } # => verify succeeds
+ # 3.times { object.expected_method }
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).times(2..4)
- # object.expected_method # => verify fails
+ # object.expected_method
+ # # => verify fails
def times(range)
- @count = range
+ @expected_count = range
+ self
+ end
+
+ # :call-seq: once() -> expectation
+ #
+ # Modifies expectation so that the expected method must be called exactly once.
+ # Note that this is the default behaviour for an expectation, but you may wish to use it for clarity/emphasis.
+ # object = mock()
+ # object.expects(:expected_method).once
+ # object.expected_method
+ # # => verify succeeds
+ #
+ # object = mock()
+ # object.expects(:expected_method).once
+ # object.expected_method
+ # object.expected_method
+ # # => verify fails
+ #
+ # object = mock()
+ # object.expects(:expected_method).once
+ # # => verify fails
+ def once()
+ times(1)
self
end
- # :call-seq: never -> expectation
+ # :call-seq: never() -> expectation
#
# Modifies expectation so that the expected method must never be called.
# object = mock()
# object.expects(:expected_method).never
- # object.expected_method # => verify fails
+ # object.expected_method
+ # # => verify fails
#
# object = mock()
# object.expects(:expected_method).never
- # object.expected_method # => verify succeeds
+ # object.expected_method
+ # # => verify succeeds
def never
times(0)
self
@@ -92,11 +121,13 @@ module Mocha
# Modifies expectation so that the expected method must be called at least a +minimum_number_of_times+.
# object = mock()
# object.expects(:expected_method).at_least(2)
- # 3.times { object.expected_method } # => verify succeeds
+ # 3.times { object.expected_method }
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).at_least(2)
- # object.expected_method # => verify fails
+ # object.expected_method
+ # # => verify fails
def at_least(minimum_number_of_times)
times(Range.at_least(minimum_number_of_times))
self
@@ -107,7 +138,8 @@ module Mocha
# Modifies expectation so that the expected method must be called at least once.
# object = mock()
# object.expects(:expected_method).at_least_once
- # object.expected_method # => verify succeeds
+ # object.expected_method
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).at_least_once
@@ -122,11 +154,13 @@ module Mocha
# Modifies expectation so that the expected method must be called at most a +maximum_number_of_times+.
# object = mock()
# object.expects(:expected_method).at_most(2)
- # 2.times { object.expected_method } # => verify succeeds
+ # 2.times { object.expected_method }
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).at_most(2)
- # 3.times { object.expected_method } # => verify fails
+ # 3.times { object.expected_method }
+ # # => verify fails
def at_most(maximum_number_of_times)
times(Range.at_most(maximum_number_of_times))
self
@@ -137,11 +171,13 @@ module Mocha
# Modifies expectation so that the expected method must be called at most once.
# object = mock()
# object.expects(:expected_method).at_most_once
- # object.expected_method # => verify succeeds
+ # object.expected_method
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).at_most_once
- # 2.times { object.expected_method } # => verify fails
+ # 2.times { object.expected_method }
+ # # => verify fails
def at_most_once()
at_most(1)
self
@@ -152,20 +188,26 @@ module Mocha
# Modifies expectation so that the expected method must be called with specified +arguments+.
# object = mock()
# object.expects(:expected_method).with(:param1, :param2)
- # object.expected_method(:param1, :param2) # => verify succeeds
+ # object.expected_method(:param1, :param2)
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).with(:param1, :param2)
- # object.expected_method(:param3) # => verify fails
+ # object.expected_method(:param3)
+ # # => verify fails
+ # May be used with parameter matchers in Mocha::ParameterMatchers.
+ #
# If a +parameter_block+ is given, the block is called with the parameters passed to the expected method.
# The expectation is matched if the block evaluates to +true+.
# object = mock()
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
- # object.expected_method(16) # => verify succeeds
+ # object.expected_method(16)
+ # # => verify succeeds
#
# object = mock()
# object.expects(:expected_method).with() { |value| value % 4 == 0 }
- # object.expected_method(17) # => verify fails
+ # object.expected_method(17)
+ # # => verify fails
def with(*arguments, &parameter_block)
@parameters, @parameter_block = arguments, parameter_block
class << @parameters; def to_s; join(', '); end; end
@@ -180,12 +222,42 @@ module Mocha
# yielded_value = nil
# object.expected_method { |value| yielded_value = value }
# yielded_value # => 'result'
+ # May be called multiple times on the same expectation for consecutive invocations. Also see Expectation#then.
+ # object = mock()
+ # object.stubs(:expected_method).yields(1).then.yields(2)
+ # yielded_values_from_first_invocation = []
+ # yielded_values_from_second_invocation = []
+ # object.expected_method { |value| yielded_values_from_first_invocation << value } # first invocation
+ # object.expected_method { |value| yielded_values_from_second_invocation << value } # second invocation
+ # yielded_values_from_first_invocation # => [1]
+ # yielded_values_from_second_invocation # => [2]
def yields(*parameters)
- @yield = true
- @parameters_to_yield = parameters
+ @yield_parameters.add(*parameters)
self
end
-
+
+ # :call-seq: multiple_yields(*parameter_groups) -> expectation
+ #
+ # Modifies expectation so that when the expected method is called, it yields multiple times per invocation with the specified +parameter_groups+.
+ # object = mock()
+ # object.expects(:expected_method).multiple_yields(['result_1', 'result_2'], ['result_3'])
+ # yielded_values = []
+ # object.expected_method { |*values| yielded_values << values }
+ # yielded_values # => [['result_1', 'result_2'], ['result_3]]
+ # May be called multiple times on the same expectation for consecutive invocations. Also see Expectation#then.
+ # object = mock()
+ # object.stubs(:expected_method).multiple_yields([1, 2], [3]).then.multiple_yields([4], [5, 6])
+ # yielded_values_from_first_invocation = []
+ # yielded_values_from_second_invocation = []
+ # object.expected_method { |*values| yielded_values_from_first_invocation << values } # first invocation
+ # object.expected_method { |*values| yielded_values_from_second_invocation << values } # second invocation
+ # yielded_values_from_first_invocation # => [[1, 2], [3]]
+ # yielded_values_from_second_invocation # => [[4], [5, 6]]
+ def multiple_yields(*parameter_groups)
+ @yield_parameters.multiple_add(*parameter_groups)
+ self
+ end
+
# :call-seq: returns(value) -> expectation
# :call-seq: returns(*values) -> expectation
#
@@ -199,13 +271,30 @@ module Mocha
# object.stubs(:stubbed_method).returns(1, 2)
# object.stubbed_method # => 1
# object.stubbed_method # => 2
- # If +value+ is a Proc, then expected method will return result of calling Proc.
+ # May be called multiple times on the same expectation. Also see Expectation#then.
+ # object = mock()
+ # object.stubs(:expected_method).returns(1, 2).then.returns(3)
+ # object.expected_method # => 1
+ # object.expected_method # => 2
+ # object.expected_method # => 3
+ # May be called in conjunction with Expectation#raises on the same expectation.
+ # object = mock()
+ # object.stubs(:expected_method).returns(1, 2).then.raises(Exception)
+ # object.expected_method # => 1
+ # object.expected_method # => 2
+ # object.expected_method # => raises exception of class Exception1
+ # If +value+ is a +Proc+, then the expected method will return the result of calling <tt>Proc#call</tt>.
+ #
+ # This usage is _deprecated_.
+ # Use explicit multiple return values and/or multiple expectations instead.
+ #
+ # A +Proc+ instance will be treated the same as any other value in a future release.
# object = mock()
# object.stubs(:stubbed_method).returns(lambda { rand(100) })
# object.stubbed_method # => 41
# object.stubbed_method # => 77
def returns(*values)
- @return_value = (values.size > 1) ? lambda { values.shift } : @return_value = values.first
+ @return_values += ReturnValues.build(*values)
self
end
@@ -215,23 +304,51 @@ module Mocha
# object = mock()
# object.expects(:expected_method).raises(Exception, 'message')
# object.expected_method # => raises exception of class Exception and with message 'message'
+ # May be called multiple times on the same expectation. Also see Expectation#then.
+ # object = mock()
+ # object.stubs(:expected_method).raises(Exception1).then.raises(Exception2)
+ # object.expected_method # => raises exception of class Exception1
+ # object.expected_method # => raises exception of class Exception2
+ # May be called in conjunction with Expectation#returns on the same expectation.
+ # object = mock()
+ # object.stubs(:expected_method).raises(Exception).then.returns(2, 3)
+ # object.expected_method # => raises exception of class Exception1
+ # object.expected_method # => 2
+ # object.expected_method # => 3
def raises(exception = RuntimeError, message = nil)
- @return_value = message ? lambda { raise exception, message } : lambda { raise exception }
+ @return_values += ReturnValues.new(ExceptionRaiser.new(exception, message))
self
end
+ # :call-seq: then() -> expectation
+ #
+ # Syntactic sugar to improve readability. Has no effect on state of the expectation.
+ # object = mock()
+ # object.stubs(:expected_method).returns(1, 2).then.raises(Exception).then.returns(4)
+ # object.expected_method # => 1
+ # object.expected_method # => 2
+ # object.expected_method # => raises exception of class Exception
+ # object.expected_method # => 4
+ def then
+ self
+ end
+
# :stopdoc:
def invoke
- @invoked += 1
- yield(*@parameters_to_yield) if yield? and block_given?
- @return_value.__is_a__(Proc) ? @return_value.call : @return_value
+ @invoked_count += 1
+ if block_given? then
+ @yield_parameters.next_invocation.each do |yield_parameters|
+ yield(*yield_parameters)
+ end
+ end
+ @return_values.next
end
def verify
yield(self) if block_given?
- unless (@count === @invoked) then
- error = ExpectationError.new(error_message(@count, @invoked))
+ unless (@expected_count === @invoked_count) then
+ error = ExpectationError.new(error_message(@expected_count, @invoked_count))
error.set_backtrace(filtered_backtrace)
raise error
end
@@ -251,45 +368,11 @@ module Mocha
end
def error_message(expected_count, actual_count)
- "#{@mock.mocha_inspect}.#{method_signature} - expected calls: #{expected_count}, actual calls: #{actual_count}"
+ "#{@mock.mocha_inspect}.#{method_signature} - expected calls: #{expected_count.mocha_inspect}, actual calls: #{actual_count}"
end
# :startdoc:
end
- # :stopdoc:
-
- class Stub < Expectation
-
- def verify
- true
- end
-
- end
-
- class MissingExpectation < Expectation
-
- def initialize(mock, method_name)
- super
- @invoked = true
- end
-
- def verify
- msg = error_message(0, 1)
- similar_expectations_list = similar_expectations.collect { |expectation| expectation.method_signature }.join("\n")
- msg << "\nSimilar expectations:\n#{similar_expectations_list}" unless similar_expectations.empty?
- error = ExpectationError.new(msg)
- error.set_backtrace(filtered_backtrace)
- raise error if @invoked
- end
-
- def similar_expectations
- @mock.expectations.select { |expectation| expectation.method_name == self.method_name }
- end
-
- end
-
- # :startdoc:
-
end \ No newline at end of file
diff --git a/test/lib/mocha/infinite_range.rb b/test/lib/mocha/infinite_range.rb
index 144861ada..05dfe559e 100644
--- a/test/lib/mocha/infinite_range.rb
+++ b/test/lib/mocha/infinite_range.rb
@@ -12,15 +12,13 @@ class Range
1/0.0
end
- alias_method :__to_s__, :to_s
-
- def to_s
- if first.to_f.infinite? then
+ def mocha_inspect
+ if first.respond_to?(:to_f) and first.to_f.infinite? then
return "at most #{last}"
- elsif last.to_f.infinite? then
+ elsif last.respond_to?(:to_f) and last.to_f.infinite? then
return "at least #{first}"
else
- __to_s__
+ to_s
end
end
diff --git a/test/lib/mocha/inspect.rb b/test/lib/mocha/inspect.rb
index fb3926b65..ad82ef70e 100644
--- a/test/lib/mocha/inspect.rb
+++ b/test/lib/mocha/inspect.rb
@@ -2,7 +2,9 @@ require 'date'
class Object
def mocha_inspect
- inspect =~ /#</ ? "#<#{self.class}:0x#{self.__id__.to_s(16)}>" : inspect
+ address = self.__id__ * 2
+ address += 0x100000000 if address < 0
+ inspect =~ /#</ ? "#<#{self.class}:0x#{'%x' % address}>" : inspect
end
end
diff --git a/test/lib/mocha/is_a.rb b/test/lib/mocha/is_a.rb
new file mode 100644
index 000000000..ee23c86a9
--- /dev/null
+++ b/test/lib/mocha/is_a.rb
@@ -0,0 +1,9 @@
+class Object
+
+ # :stopdoc:
+
+ alias_method :__is_a__, :is_a?
+
+ # :startdoc:
+
+end
diff --git a/test/lib/mocha/missing_expectation.rb b/test/lib/mocha/missing_expectation.rb
new file mode 100644
index 000000000..f84227d1a
--- /dev/null
+++ b/test/lib/mocha/missing_expectation.rb
@@ -0,0 +1,27 @@
+require 'mocha/expectation'
+
+module Mocha # :nodoc:
+
+ class MissingExpectation < Expectation # :nodoc:
+
+ def initialize(mock, method_name)
+ super
+ @invoked_count = true
+ end
+
+ def verify
+ msg = error_message(0, 1)
+ similar_expectations_list = similar_expectations.collect { |expectation| expectation.method_signature }.join("\n")
+ msg << "\nSimilar expectations:\n#{similar_expectations_list}" unless similar_expectations.empty?
+ error = ExpectationError.new(msg)
+ error.set_backtrace(filtered_backtrace)
+ raise error if @invoked_count
+ end
+
+ def similar_expectations
+ @mock.expectations.select { |expectation| expectation.method_name == self.method_name }
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/mock.rb b/test/lib/mocha/mock.rb
index 1924aa89c..18c23fede 100644
--- a/test/lib/mocha/mock.rb
+++ b/test/lib/mocha/mock.rb
@@ -1,19 +1,206 @@
-require 'mocha/mock_methods'
+require 'mocha/expectation'
+require 'mocha/stub'
+require 'mocha/missing_expectation'
+require 'mocha/metaclass'
-module Mocha
+module Mocha # :nodoc:
+ # Traditional mock object.
+ #
+ # Methods return an Expectation which can be further modified by methods on Expectation.
class Mock
- include MockMethods
-
+ # :stopdoc:
+
def initialize(stub_everything = false, name = nil)
@stub_everything = stub_everything
@mock_name = name
+ @expectations = []
+ @responder = nil
+ end
+
+ attr_reader :stub_everything, :expectations
+
+ # :startdoc:
+
+ # :call-seq: expects(method_name) -> expectation
+ # expects(method_names) -> last expectation
+ #
+ # Adds an expectation that a method identified by +method_name+ symbol must be called exactly once with any parameters.
+ # Returns the new expectation which can be further modified by methods on Expectation.
+ # object = mock()
+ # object.expects(:method1)
+ # object.method1
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method1)
+ # # error raised, because method1 not called exactly once
+ # If +method_names+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
+ # object = mock()
+ # object.expects(:method1 => :result1, :method2 => :result2)
+ #
+ # # exactly equivalent to
+ #
+ # object = mock()
+ # object.expects(:method1).returns(:result1)
+ # object.expects(:method2).returns(:result2)
+ #
+ # Aliased by <tt>\_\_expects\_\_</tt>
+ def expects(method_name_or_hash, backtrace = nil)
+ if method_name_or_hash.is_a?(Hash) then
+ method_name_or_hash.each do |method_name, return_value|
+ add_expectation(Expectation.new(self, method_name, backtrace).returns(return_value))
+ end
+ else
+ add_expectation(Expectation.new(self, method_name_or_hash, backtrace))
+ end
+ end
+
+ # :call-seq: stubs(method_name) -> expectation
+ # stubs(method_names) -> last expectation
+ #
+ # Adds an expectation that a method identified by +method_name+ symbol may be called any number of times with any parameters.
+ # Returns the new expectation which can be further modified by methods on Expectation.
+ # object = mock()
+ # object.stubs(:method1)
+ # object.method1
+ # object.method1
+ # # no error raised
+ # If +method_names+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
+ # object = mock()
+ # object.stubs(:method1 => :result1, :method2 => :result2)
+ #
+ # # exactly equivalent to
+ #
+ # object = mock()
+ # object.stubs(:method1).returns(:result1)
+ # object.stubs(:method2).returns(:result2)
+ #
+ # Aliased by <tt>\_\_stubs\_\_</tt>
+ def stubs(method_name_or_hash, backtrace = nil)
+ if method_name_or_hash.is_a?(Hash) then
+ method_name_or_hash.each do |method_name, return_value|
+ add_expectation(Stub.new(self, method_name, backtrace).returns(return_value))
+ end
+ else
+ add_expectation(Stub.new(self, method_name_or_hash, backtrace))
+ end
+ end
+
+ # :call-seq: responds_like(responder) -> mock
+ #
+ # Constrains the +mock+ so that it can only expect or stub methods to which +responder+ responds. The constraint is only applied at method invocation time.
+ #
+ # A +NoMethodError+ will be raised if the +responder+ does not <tt>respond_to?</tt> a method invocation (even if the method has been expected or stubbed).
+ #
+ # The +mock+ will delegate its <tt>respond_to?</tt> method to the +responder+.
+ # class Sheep
+ # def chew(grass); end
+ # def self.number_of_legs; end
+ # end
+ #
+ # sheep = mock('sheep')
+ # sheep.expects(:chew)
+ # sheep.expects(:foo)
+ # sheep.respond_to?(:chew) # => true
+ # sheep.respond_to?(:foo) # => true
+ # sheep.chew
+ # sheep.foo
+ # # no error raised
+ #
+ # sheep = mock('sheep')
+ # sheep.responds_like(Sheep.new)
+ # sheep.expects(:chew)
+ # sheep.expects(:foo)
+ # sheep.respond_to?(:chew) # => true
+ # sheep.respond_to?(:foo) # => false
+ # sheep.chew
+ # sheep.foo # => raises NoMethodError exception
+ #
+ # sheep_class = mock('sheep_class')
+ # sheep_class.responds_like(Sheep)
+ # sheep_class.stubs(:number_of_legs).returns(4)
+ # sheep_class.expects(:foo)
+ # sheep_class.respond_to?(:number_of_legs) # => true
+ # sheep_class.respond_to?(:foo) # => false
+ # assert_equal 4, sheep_class.number_of_legs
+ # sheep_class.foo # => raises NoMethodError exception
+ #
+ # Aliased by +quacks_like+
+ def responds_like(object)
+ @responder = object
+ self
end
+
+ # :stopdoc:
+
+ alias_method :__expects__, :expects
+ alias_method :__stubs__, :stubs
+
+ alias_method :quacks_like, :responds_like
+
+ def add_expectation(expectation)
+ @expectations << expectation
+ method_name = expectation.method_name
+ self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
+ expectation
+ end
+
+ def method_missing(symbol, *arguments, &block)
+ if @responder and not @responder.respond_to?(symbol)
+ raise NoMethodError, "undefined method `#{symbol}' for #{self.mocha_inspect} which responds like #{@responder.mocha_inspect}"
+ end
+ matching_expectation = matching_expectation(symbol, *arguments)
+ if matching_expectation then
+ matching_expectation.invoke(&block)
+ elsif stub_everything then
+ return
+ else
+ begin
+ super_method_missing(symbol, *arguments, &block)
+ rescue NoMethodError
+ unexpected_method_called(symbol, *arguments)
+ end
+ end
+ end
+
+ def respond_to?(symbol)
+ if @responder then
+ @responder.respond_to?(symbol)
+ else
+ @expectations.any? { |expectation| expectation.method_name == symbol }
+ end
+ end
+
+ def super_method_missing(symbol, *arguments, &block)
+ raise NoMethodError
+ end
+
+ def unexpected_method_called(symbol, *arguments)
+ MissingExpectation.new(self, symbol).with(*arguments).verify
+ end
+
+ def matching_expectation(symbol, *arguments)
+ @expectations.reverse.detect { |expectation| expectation.match?(symbol, *arguments) }
+ end
+
+ def verify(&block)
+ @expectations.each { |expectation| expectation.verify(&block) }
+ end
+
def mocha_inspect
- @mock_name ? "#<Mock:#{@mock_name}>" : "#<Mock:0x#{__id__.to_s(16)}>"
+ address = self.__id__ * 2
+ address += 0x100000000 if address < 0
+ @mock_name ? "#<Mock:#{@mock_name}>" : "#<Mock:0x#{'%x' % address}>"
end
+
+ def inspect
+ mocha_inspect
+ end
+
+ # :startdoc:
end
diff --git a/test/lib/mocha/mock_methods.rb b/test/lib/mocha/mock_methods.rb
deleted file mode 100644
index e2f5a114e..000000000
--- a/test/lib/mocha/mock_methods.rb
+++ /dev/null
@@ -1,122 +0,0 @@
-require 'mocha/expectation'
-require 'mocha/metaclass'
-
-module Mocha
- # Methods added to mock objects.
- # These methods all return an expectation which can be further modified by methods on Mocha::Expectation.
- module MockMethods
-
- # :stopdoc:
-
- attr_reader :stub_everything
-
- def expectations
- @expectations ||= []
- end
-
- # :startdoc:
-
- # :call-seq: expects(method_name) -> expectation
- # expects(method_names) -> last expectation
- #
- # Adds an expectation that a method identified by +method_name+ symbol must be called exactly once with any parameters.
- # Returns the new expectation which can be further modified by methods on Mocha::Expectation.
- # object = mock()
- # object.expects(:method1)
- # object.method1
- # # no error raised
- #
- # object = mock()
- # object.expects(:method1)
- # # error raised, because method1 not called exactly once
- # If +method_names+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
- # object = mock()
- # object.expects(:method1 => :result1, :method2 => :result2)
- #
- # # exactly equivalent to
- #
- # object = mock()
- # object.expects(:method1).returns(:result1)
- # object.expects(:method2).returns(:result2)
- def expects(method_names, backtrace = nil)
- method_names = method_names.is_a?(Hash) ? method_names : { method_names => nil }
- method_names.each do |method_name, return_value|
- expectations << Expectation.new(self, method_name, backtrace).returns(return_value)
- self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
- end
- expectations.last
- end
-
- alias_method :__expects__, :expects
-
- # :call-seq: stubs(method_name) -> expectation
- # stubs(method_names) -> last expectation
- #
- # Adds an expectation that a method identified by +method_name+ symbol may be called any number of times with any parameters.
- # Returns the new expectation which can be further modified by methods on Mocha::Expectation.
- # object = mock()
- # object.stubs(:method1)
- # object.method1
- # object.method1
- # # no error raised
- # If +method_names+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
- # object = mock()
- # object.stubs(:method1 => :result1, :method2 => :result2)
- #
- # # exactly equivalent to
- #
- # object = mock()
- # object.stubs(:method1).returns(:result1)
- # object.stubs(:method2).returns(:result2)
- def stubs(method_names, backtrace = nil)
- method_names = method_names.is_a?(Hash) ? method_names : { method_names => nil }
- method_names.each do |method_name, return_value|
- expectations << Stub.new(self, method_name, backtrace).returns(return_value)
- self.__metaclass__.send(:undef_method, method_name) if self.__metaclass__.method_defined?(method_name)
- end
- expectations.last
- end
-
- alias_method :__stubs__, :stubs
-
- # :stopdoc:
-
- def method_missing(symbol, *arguments, &block)
- matching_expectation = matching_expectation(symbol, *arguments)
- if matching_expectation then
- matching_expectation.invoke(&block)
- elsif stub_everything then
- return
- else
- begin
- super_method_missing(symbol, *arguments, &block)
- rescue NoMethodError
- unexpected_method_called(symbol, *arguments)
- end
- end
- end
-
- def respond_to?(symbol)
- expectations.any? { |expectation| expectation.method_name == symbol }
- end
-
- def super_method_missing(symbol, *arguments, &block)
- raise NoMethodError
- end
-
- def unexpected_method_called(symbol, *arguments)
- MissingExpectation.new(self, symbol).with(*arguments).verify
- end
-
- def matching_expectation(symbol, *arguments)
- expectations.reverse.detect { |expectation| expectation.match?(symbol, *arguments) }
- end
-
- def verify(&block)
- expectations.each { |expectation| expectation.verify(&block) }
- end
-
- # :startdoc:
-
- end
-end \ No newline at end of file
diff --git a/test/lib/mocha/multiple_yields.rb b/test/lib/mocha/multiple_yields.rb
new file mode 100644
index 000000000..8186c3076
--- /dev/null
+++ b/test/lib/mocha/multiple_yields.rb
@@ -0,0 +1,20 @@
+module Mocha # :nodoc:
+
+ class MultipleYields # :nodoc:
+
+ attr_reader :parameter_groups
+
+ def initialize(*parameter_groups)
+ @parameter_groups = parameter_groups
+ end
+
+ def each
+ @parameter_groups.each do |parameter_group|
+ yield(parameter_group)
+ end
+ end
+
+ end
+
+end
+
diff --git a/test/lib/mocha/no_yields.rb b/test/lib/mocha/no_yields.rb
new file mode 100644
index 000000000..b0fba415d
--- /dev/null
+++ b/test/lib/mocha/no_yields.rb
@@ -0,0 +1,11 @@
+module Mocha # :nodoc:
+
+ class NoYields # :nodoc:
+
+ def each
+ end
+
+ end
+
+end
+
diff --git a/test/lib/mocha/object.rb b/test/lib/mocha/object.rb
index 7b1d6b83e..7ccdbad0d 100644
--- a/test/lib/mocha/object.rb
+++ b/test/lib/mocha/object.rb
@@ -3,7 +3,9 @@ require 'mocha/instance_method'
require 'mocha/class_method'
require 'mocha/any_instance_method'
-# Methods added all Objects.
+# Methods added all objects to allow mocking and stubbing on real objects.
+#
+# Methods return a Mocha::Expectation which can be further modified by methods on Mocha::Expectation.
class Object
def mocha # :nodoc:
@@ -29,6 +31,10 @@ class Object
# product = Product.new
# product.expects(:save).returns(true)
# assert_equal false, product.save
+ #
+ # The original implementation of <tt>Product#save</tt> is replaced temporarily.
+ #
+ # The original implementation of <tt>Product#save</tt> is restored at the end of the test.
def expects(symbol)
method = stubba_method.new(stubba_object, symbol)
$stubba.stub(method)
@@ -42,6 +48,10 @@ class Object
# product = Product.new
# product.stubs(:save).returns(true)
# assert_equal false, product.save
+ #
+ # The original implementation of <tt>Product#save</tt> is replaced temporarily.
+ #
+ # The original implementation of <tt>Product#save</tt> is restored at the end of the test.
def stubs(symbol)
method = stubba_method.new(stubba_object, symbol)
$stubba.stub(method)
diff --git a/test/lib/mocha/parameter_matchers.rb b/test/lib/mocha/parameter_matchers.rb
new file mode 100644
index 000000000..193f77d93
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers.rb
@@ -0,0 +1,9 @@
+module Mocha
+
+ # Used as parameters for Expectation#with to restrict the parameter values which will match the expectation.
+ module ParameterMatchers; end
+
+end
+
+
+Dir[File.expand_path(File.join(File.dirname(__FILE__), 'parameter_matchers', "*.rb"))].each { |lib| require lib }
diff --git a/test/lib/mocha/parameter_matchers/all_of.rb b/test/lib/mocha/parameter_matchers/all_of.rb
new file mode 100644
index 000000000..343d9eea0
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/all_of.rb
@@ -0,0 +1,39 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: all_of -> parameter_matcher
+ #
+ # Matches if all +matchers+ match.
+ # object = mock()
+ # object.expects(:method_1).with(all_of(includes(1), includes(3)))
+ # object.method_1([1, 3])
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method_1).with(all_of(includes(1), includes(3)))
+ # object.method_1([1, 2])
+ # # error raised, because method_1 was not called with object including 1 and 3
+ def all_of(*matchers)
+ AllOf.new(*matchers)
+ end
+
+ class AllOf # :nodoc:
+
+ def initialize(*matchers)
+ @matchers = matchers
+ end
+
+ def ==(parameter)
+ @matchers.all? { |matcher| matcher == parameter }
+ end
+
+ def mocha_inspect
+ "all_of(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
+ end
+
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/parameter_matchers/any_of.rb b/test/lib/mocha/parameter_matchers/any_of.rb
new file mode 100644
index 000000000..a1f88075d
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/any_of.rb
@@ -0,0 +1,44 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: any_of -> parameter_matcher
+ #
+ # Matches if any +matchers+ match.
+ # object = mock()
+ # object.expects(:method_1).with(any_of(1, 3))
+ # object.method_1(1)
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method_1).with(any_of(1, 3))
+ # object.method_1(3)
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method_1).with(any_of(1, 3))
+ # object.method_1(2)
+ # # error raised, because method_1 was not called with 1 or 3
+ def any_of(*matchers)
+ AnyOf.new(*matchers)
+ end
+
+ class AnyOf # :nodoc:
+
+ def initialize(*matchers)
+ @matchers = matchers
+ end
+
+ def ==(parameter)
+ @matchers.any? { |matcher| matcher == parameter }
+ end
+
+ def mocha_inspect
+ "any_of(#{@matchers.map { |matcher| matcher.mocha_inspect }.join(", ") })"
+ end
+
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/parameter_matchers/anything.rb b/test/lib/mocha/parameter_matchers/anything.rb
new file mode 100644
index 000000000..57d0eeab4
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/anything.rb
@@ -0,0 +1,30 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: anything -> parameter_matcher
+ #
+ # Matches any object.
+ # object = mock()
+ # object.expects(:method_1).with(anything)
+ # object.method_1('foo')
+ # # no error raised
+ def anything
+ Anything.new
+ end
+
+ class Anything # :nodoc:
+
+ def ==(parameter)
+ return true
+ end
+
+ def mocha_inspect
+ "anything"
+ end
+
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/parameter_matchers/has_entry.rb b/test/lib/mocha/parameter_matchers/has_entry.rb
new file mode 100644
index 000000000..3d7cac4e6
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/has_entry.rb
@@ -0,0 +1,39 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: has_entry(key, value) -> parameter_matcher
+ #
+ # Matches +Hash+ containing entry with +key+ and +value+.
+ # object = mock()
+ # object.expects(:method_1).with(has_entry('key_1', 1))
+ # object.method_1('key_1' => 1, 'key_2' => 2)
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method_1).with(has_entry('key_1', 1))
+ # object.method_1('key_1' => 2, 'key_2' => 1)
+ # # error raised, because method_1 was not called with Hash containing entry: 'key_1' => 1
+ def has_entry(key, value)
+ HasEntry.new(key, value)
+ end
+
+ class HasEntry # :nodoc:
+
+ def initialize(key, value)
+ @key, @value = key, value
+ end
+
+ def ==(parameter)
+ parameter[@key] == @value
+ end
+
+ def mocha_inspect
+ "has_entry(#{@key.mocha_inspect}, #{@value.mocha_inspect})"
+ end
+
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/parameter_matchers/has_key.rb b/test/lib/mocha/parameter_matchers/has_key.rb
new file mode 100644
index 000000000..5a1fcd2e8
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/has_key.rb
@@ -0,0 +1,39 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: has_key(key) -> parameter_matcher
+ #
+ # Matches +Hash+ containing +key+.
+ # object = mock()
+ # object.expects(:method_1).with(has_key('key_1'))
+ # object.method_1('key_1' => 1, 'key_2' => 2)
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method_1).with(has_key('key_1'))
+ # object.method_1('key_2' => 2)
+ # # error raised, because method_1 was not called with Hash containing key: 'key_1'
+ def has_key(key)
+ HasKey.new(key)
+ end
+
+ class HasKey # :nodoc:
+
+ def initialize(key)
+ @key = key
+ end
+
+ def ==(parameter)
+ parameter.keys.include?(@key)
+ end
+
+ def mocha_inspect
+ "has_key(#{@key.mocha_inspect})"
+ end
+
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/parameter_matchers/has_value.rb b/test/lib/mocha/parameter_matchers/has_value.rb
new file mode 100644
index 000000000..742f84268
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/has_value.rb
@@ -0,0 +1,39 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: has_value(value) -> parameter_matcher
+ #
+ # Matches +Hash+ containing +value+.
+ # object = mock()
+ # object.expects(:method_1).with(has_value(1))
+ # object.method_1('key_1' => 1, 'key_2' => 2)
+ # # no error raised
+ #
+ # object = mock()
+ # object.expects(:method_1).with(has_value(1))
+ # object.method_1('key_2' => 2)
+ # # error raised, because method_1 was not called with Hash containing value: 1
+ def has_value(value)
+ HasValue.new(value)
+ end
+
+ class HasValue # :nodoc:
+
+ def initialize(value)
+ @value = value
+ end
+
+ def ==(parameter)
+ parameter.values.include?(@value)
+ end
+
+ def mocha_inspect
+ "has_value(#{@value.mocha_inspect})"
+ end
+
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/parameter_matchers/includes.rb b/test/lib/mocha/parameter_matchers/includes.rb
new file mode 100644
index 000000000..0e4fbe960
--- /dev/null
+++ b/test/lib/mocha/parameter_matchers/includes.rb
@@ -0,0 +1,37 @@
+module Mocha
+
+ module ParameterMatchers
+
+ # :call-seq: includes(item) -> parameter_matcher
+ #
+ # Matches any object that responds true to include?(item)
+ # object = mock()
+ # object.expects(:method_1).with(includes('foo'))
+ # object.method_1(['foo', 'bar'])
+ # # no error raised
+ #
+ # object.method_1(['baz'])
+ # # error raised, because ['baz'] does not include 'foo'.
+ def includes(item)
+ Includes.new(item)
+ end
+
+ class Includes # :nodoc:
+
+ def initialize(item)
+ @item = item
+ end
+
+ def ==(parameter)
+ return parameter.include?(@item)
+ end
+
+ def mocha_inspect
+ "includes(#{@item.mocha_inspect})"
+ end
+
+ end
+
+ end
+
+end
diff --git a/test/lib/mocha/return_values.rb b/test/lib/mocha/return_values.rb
new file mode 100644
index 000000000..ea0fbbd40
--- /dev/null
+++ b/test/lib/mocha/return_values.rb
@@ -0,0 +1,31 @@
+require 'mocha/single_return_value'
+
+module Mocha # :nodoc:
+
+ class ReturnValues # :nodoc:
+
+ def self.build(*values)
+ new(*values.map { |value| SingleReturnValue.new(value) })
+ end
+
+ attr_accessor :values
+
+ def initialize(*values)
+ @values = values
+ end
+
+ def next
+ case @values.size
+ when 0: nil
+ when 1: @values.first.evaluate
+ else @values.shift.evaluate
+ end
+ end
+
+ def +(other)
+ self.class.new(*(@values + other.values))
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/single_return_value.rb b/test/lib/mocha/single_return_value.rb
new file mode 100644
index 000000000..f420b8b8c
--- /dev/null
+++ b/test/lib/mocha/single_return_value.rb
@@ -0,0 +1,24 @@
+require 'mocha/is_a'
+require 'mocha/deprecation'
+
+module Mocha # :nodoc:
+
+ class SingleReturnValue # :nodoc:
+
+ def initialize(value)
+ @value = value
+ end
+
+ def evaluate
+ if @value.__is_a__(Proc) then
+ message = 'use of Expectation#returns with instance of Proc - see Expectation#returns RDoc for alternatives'
+ Deprecation.warning(message)
+ @value.call
+ else
+ @value
+ end
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/single_yield.rb b/test/lib/mocha/single_yield.rb
new file mode 100644
index 000000000..5af571621
--- /dev/null
+++ b/test/lib/mocha/single_yield.rb
@@ -0,0 +1,18 @@
+module Mocha # :nodoc:
+
+ class SingleYield # :nodoc:
+
+ attr_reader :parameters
+
+ def initialize(*parameters)
+ @parameters = parameters
+ end
+
+ def each
+ yield(@parameters)
+ end
+
+ end
+
+end
+
diff --git a/test/lib/mocha/standalone.rb b/test/lib/mocha/standalone.rb
index 127ede641..8e3a7cefc 100644
--- a/test/lib/mocha/standalone.rb
+++ b/test/lib/mocha/standalone.rb
@@ -1,4 +1,5 @@
require 'mocha/auto_verify'
+require 'mocha/parameter_matchers'
require 'mocha/setup_and_teardown'
module Mocha
@@ -6,6 +7,7 @@ module Mocha
module Standalone
include AutoVerify
+ include ParameterMatchers
include SetupAndTeardown
def mocha_setup
diff --git a/test/lib/mocha/stub.rb b/test/lib/mocha/stub.rb
new file mode 100644
index 000000000..1b3cccb8a
--- /dev/null
+++ b/test/lib/mocha/stub.rb
@@ -0,0 +1,18 @@
+require 'mocha/expectation'
+
+module Mocha # :nodoc:
+
+ class Stub < Expectation # :nodoc:
+
+ def initialize(mock, method_name, backtrace = nil)
+ super
+ @expected_count = Range.at_least(0)
+ end
+
+ def verify
+ true
+ end
+
+ end
+
+end \ No newline at end of file
diff --git a/test/lib/mocha/test_case_adapter.rb b/test/lib/mocha/test_case_adapter.rb
index 100a7a514..dc7e33b68 100644
--- a/test/lib/mocha/test_case_adapter.rb
+++ b/test/lib/mocha/test_case_adapter.rb
@@ -22,8 +22,8 @@ module Mocha
add_failure(e.message, e.backtrace)
rescue Test::Unit::AssertionFailedError => e
add_failure(e.message, e.backtrace)
- rescue StandardError, ScriptError => e
- add_error(e)
+ rescue StandardError, ScriptError
+ add_error($!)
ensure
begin
teardown
@@ -46,4 +46,4 @@ module Mocha
end
-end
+end \ No newline at end of file
diff --git a/test/lib/mocha/yield_parameters.rb b/test/lib/mocha/yield_parameters.rb
new file mode 100644
index 000000000..b1623bf71
--- /dev/null
+++ b/test/lib/mocha/yield_parameters.rb
@@ -0,0 +1,31 @@
+require 'mocha/no_yields'
+require 'mocha/single_yield'
+require 'mocha/multiple_yields'
+
+module Mocha # :nodoc:
+
+ class YieldParameters # :nodoc:
+
+ def initialize
+ @parameter_groups = []
+ end
+
+ def next_invocation
+ case @parameter_groups.size
+ when 0: NoYields.new
+ when 1: @parameter_groups.first
+ else @parameter_groups.shift
+ end
+ end
+
+ def add(*parameters)
+ @parameter_groups << SingleYield.new(*parameters)
+ end
+
+ def multiple_add(*parameter_groups)
+ @parameter_groups << MultipleYields.new(*parameter_groups)
+ end
+
+ end
+
+end \ No newline at end of file