Permalink
Browse files

As Tobias Crawley correctly pointed out in feature request #23055 "st…

…ubs(with_hash) not working with existing object" [1], following the principle of least surprise, it should be possible to call ObjectMethods#expects & ObjectMethods#stubs with a Hash of method_names vs return_values like you can with Mock#expects & Mock#stubs. I've extracted out some shared logic into the ArgumentIterator - not sure this is the best solution, but at least it keeps duplication to a minimum. I've also updated & improved the docs to reflect the changes.

[1] http://rubyforge.org/tracker/index.php?func=detail&aid=23055&group_id=1917&atid=7480
  • Loading branch information...
1 parent d4a5b3e commit 1b3ed16c7755f8a548087d2d31bbcb44e2315281 @floehopper floehopper committed Dec 2, 2008
Showing with 136 additions and 40 deletions.
  1. +21 −0 lib/mocha/argument_iterator.rb
  2. +24 −24 lib/mocha/mock.rb
  3. +53 −16 lib/mocha/object.rb
  4. +38 −0 test/acceptance/stub_instance_method_test.rb
@@ -0,0 +1,21 @@
+module Mocha
+
+ class ArgumentIterator
+
+ def initialize(argument)
+ @argument = argument
+ end
+
+ def each(&block)
+ if @argument.is_a?(Hash) then
+ @argument.each do |method_name, return_value|
+ block.call(method_name, return_value)
+ end
+ else
+ block.call(@argument)
+ end
+ end
+
+ end
+
+end
View
@@ -6,6 +6,7 @@
require 'mocha/method_matcher'
require 'mocha/parameters_matcher'
require 'mocha/unexpected_invocation'
+require 'mocha/argument_iterator'
module Mocha # :nodoc:
@@ -15,9 +16,9 @@ module Mocha # :nodoc:
class Mock
# :call-seq: expects(method_name) -> expectation
- # expects(method_names) -> last expectation
+ # expects(method_names_vs_return_values) -> last expectation
#
- # Adds an expectation that a method identified by +method_name+ symbol must be called exactly once with any parameters.
+ # Adds an expectation that a method identified by +method_name+ Symbol/String 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)
@@ -27,7 +28,7 @@ class Mock
# 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+.
+ # If +method_names_vs_return_values+ 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)
#
@@ -39,28 +40,27 @@ class Mock
#
# 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|
- ensure_method_not_already_defined(method_name)
- @expectations.add(Expectation.new(self, method_name, backtrace).returns(return_value))
- end
- else
- ensure_method_not_already_defined(method_name_or_hash)
- @expectations.add(Expectation.new(self, method_name_or_hash, backtrace))
- end
+ iterator = ArgumentIterator.new(method_name_or_hash)
+ iterator.each { |*args|
+ method_name = args.shift
+ ensure_method_not_already_defined(method_name)
+ expectation = Expectation.new(self, method_name, backtrace)
+ expectation.returns(args.shift) if args.length > 0
+ @expectations.add(expectation)
+ }
end
# :call-seq: stubs(method_name) -> expectation
- # stubs(method_names) -> last expectation
+ # stubs(method_names_vs_return_values) -> last expectation
#
- # Adds an expectation that a method identified by +method_name+ symbol may be called any number of times with any parameters.
+ # Adds an expectation that a method identified by +method_name+ Symbol/String 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+.
+ # If +method_names_vs_return_values+ 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)
#
@@ -72,15 +72,15 @@ def expects(method_name_or_hash, backtrace = nil)
#
# 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|
- ensure_method_not_already_defined(method_name)
- @expectations.add(Expectation.new(self, method_name, backtrace).at_least(0).returns(return_value))
- end
- else
- ensure_method_not_already_defined(method_name_or_hash)
- @expectations.add(Expectation.new(self, method_name_or_hash, backtrace).at_least(0))
- end
+ iterator = ArgumentIterator.new(method_name_or_hash)
+ iterator.each { |*args|
+ method_name = args.shift
+ ensure_method_not_already_defined(method_name)
+ expectation = Expectation.new(self, method_name, backtrace)
+ expectation.at_least(0)
+ expectation.returns(args.shift) if args.length > 0
+ @expectations.add(expectation)
+ }
end
# :call-seq: responds_like(responder) -> mock
View
@@ -3,6 +3,7 @@
require 'mocha/class_method'
require 'mocha/module_method'
require 'mocha/any_instance_method'
+require 'mocha/argument_iterator'
module Mocha
@@ -27,42 +28,78 @@ def stubba_object # :nodoc:
self
end
- # :call-seq: expects(symbol) -> expectation
+ # :call-seq: expects(method_name) -> expectation
+ # expects(method_names_vs_return_values) -> last expectation
#
- # Adds an expectation that a method identified by +symbol+ must be called exactly once with any parameters.
+ # 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.
# product = Product.new
# product.expects(:save).returns(true)
- # assert_equal false, product.save
+ # assert_equal true, 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)
+ #
+ # If +method_names_vs_return_values+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
+ # product = Product.new
+ # product.expects(:valid? => true, :save => true)
+ #
+ # # exactly equivalent to
+ #
+ # product = Product.new
+ # product.expects(:valid?).returns(true)
+ # product.expects(:save).returns(true)
+ def expects(method_name_or_hash)
+ expectation = nil
mockery = Mocha::Mockery.instance
- mockery.on_stubbing(self, symbol)
- method = stubba_method.new(stubba_object, symbol)
- mockery.stubba.stub(method)
- mocha.expects(symbol, caller)
+ iterator = ArgumentIterator.new(method_name_or_hash)
+ iterator.each { |*args|
+ method_name = args.shift
+ mockery.on_stubbing(self, method_name)
+ method = stubba_method.new(stubba_object, method_name)
+ mockery.stubba.stub(method)
+ expectation = mocha.expects(method_name, caller)
+ expectation.returns(args.shift) if args.length > 0
+ }
+ expectation
end
- # :call-seq: stubs(symbol) -> expectation
+ # :call-seq: stubs(method_name) -> expectation
+ # stubs(method_names_vs_return_values) -> last expectation
#
- # Adds an expectation that a method identified by +symbol+ may be called any number of times with any parameters.
+ # 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.
# product = Product.new
# product.stubs(:save).returns(true)
- # assert_equal false, product.save
+ # assert_equal true, 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)
+ #
+ # If +method_names_vs_return_values+ is a +Hash+, an expectation will be set up for each entry using the key as +method_name+ and value as +return_value+.
+ # product = Product.new
+ # product.stubs(:valid? => true, :save => true)
+ #
+ # # exactly equivalent to
+ #
+ # product = Product.new
+ # product.stubs(:valid?).returns(true)
+ # product.stubs(:save).returns(true)
+ def stubs(method_name_or_hash)
+ expectation = nil
mockery = Mocha::Mockery.instance
- mockery.on_stubbing(self, symbol)
- method = stubba_method.new(stubba_object, symbol)
- mockery.stubba.stub(method)
- mocha.stubs(symbol, caller)
+ iterator = ArgumentIterator.new(method_name_or_hash)
+ iterator.each { |*args|
+ method_name = args.shift
+ mockery.on_stubbing(self, method_name)
+ method = stubba_method.new(stubba_object, method_name)
+ mockery.stubba.stub(method)
+ expectation = mocha.stubs(method_name, caller)
+ expectation.returns(args.shift) if args.length > 0
+ }
+ expectation
end
def method_exists?(method, include_public_methods = true) # :nodoc:
@@ -162,4 +162,42 @@ def private_methods(include_superclass = true)
assert_passed(test_result)
end
+ def test_should_be_able_to_specify_expectations_on_multiple_methods_in_a_single_call_to_expects
+ instance = Class.new do
+ def my_instance_method_1
+ :original_return_value_1
+ end
+ def my_instance_method_2
+ :original_return_value_2
+ end
+ end.new
+ run_test do
+ instance.expects(
+ :my_instance_method_1 => :new_return_value_1,
+ :my_instance_method_2 => :new_return_value_2
+ )
+ assert_equal :new_return_value_1, instance.my_instance_method_1
+ assert_equal :new_return_value_2, instance.my_instance_method_2
+ end
+ end
+
+ def test_should_be_able_to_specify_expectations_on_multiple_methods_in_a_single_call_to_stubs
+ instance = Class.new do
+ def my_instance_method_1
+ :original_return_value_1
+ end
+ def my_instance_method_2
+ :original_return_value_2
+ end
+ end.new
+ run_test do
+ instance.stubs(
+ :my_instance_method_1 => :new_return_value_1,
+ :my_instance_method_2 => :new_return_value_2
+ )
+ assert_equal :new_return_value_1, instance.my_instance_method_1
+ assert_equal :new_return_value_2, instance.my_instance_method_2
+ end
+ end
+
end

0 comments on commit 1b3ed16

Please sign in to comment.