diff --git a/lib/mocha/class_methods.rb b/lib/mocha/class_methods.rb index 2ccd58fd..7b2fc32f 100644 --- a/lib/mocha/class_methods.rb +++ b/lib/mocha/class_methods.rb @@ -12,7 +12,7 @@ def initialize(klass) def mocha(instantiate = true) if instantiate - @mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object) + @mocha ||= Mocha::Mockery.instance.mock_impersonating_any_instance_of(@stubba_object).responds_like_instance_of(@stubba_object) else defined?(@mocha) ? @mocha : nil end diff --git a/lib/mocha/mock.rb b/lib/mocha/mock.rb index b448cfe0..e6e7e873 100644 --- a/lib/mocha/mock.rb +++ b/lib/mocha/mock.rb @@ -8,6 +8,7 @@ require 'mocha/parameters_matcher' require 'mocha/argument_iterator' require 'mocha/expectation_error_factory' +require 'mocha/ruby_version' module Mocha # Traditional mock object. @@ -381,9 +382,13 @@ def raise_unexpected_invocation_error(invocation, matching_expectation) end def check_responder_responds_to(symbol) - if @responder && !@responder.respond_to?(symbol) # rubocop:disable Style/GuardClause - raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}" - end + return unless @responder + + legacy_behaviour_for_array_flatten = !RUBY_V23_PLUS && !@responder.respond_to?(symbol) && (symbol == :to_ary) + + return if @responder.respond_to?(symbol, true) && !legacy_behaviour_for_array_flatten + + raise NoMethodError, "undefined method `#{symbol}' for #{mocha_inspect} which responds like #{@responder.mocha_inspect}" end def check_expiry diff --git a/lib/mocha/object_methods.rb b/lib/mocha/object_methods.rb index 4e4e5967..045a3317 100644 --- a/lib/mocha/object_methods.rb +++ b/lib/mocha/object_methods.rb @@ -14,7 +14,7 @@ module ObjectMethods # @private def mocha(instantiate = true) if instantiate - @mocha ||= Mocha::Mockery.instance.mock_impersonating(self) + @mocha ||= Mocha::Mockery.instance.mock_impersonating(self).responds_like(self) else defined?(@mocha) ? @mocha : nil end diff --git a/lib/mocha/ruby_version.rb b/lib/mocha/ruby_version.rb index 124eb472..3d8e857c 100644 --- a/lib/mocha/ruby_version.rb +++ b/lib/mocha/ruby_version.rb @@ -1,3 +1,4 @@ module Mocha + RUBY_V23_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.3') RUBY_V27_PLUS = Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('2.7') end diff --git a/test/acceptance/responds_like_test.rb b/test/acceptance/responds_like_test.rb index 35355d3a..1bc85d7c 100644 --- a/test/acceptance/responds_like_test.rb +++ b/test/acceptance/responds_like_test.rb @@ -130,14 +130,14 @@ def foo; end assert_passed(test_result) end - def test_mock_which_responds_like_object_with_protected_method_raises_no_method_error_when_method_is_not_stubbed + def test_mock_which_responds_like_object_with_protected_method_raises_unexpected_invocation_exception_when_method_is_not_stubbed object = Class.new do def foo; end protected :foo end.new test_result = run_as_test do m = mock.responds_like(object) - assert_raises(NoMethodError) { m.foo } # vs Minitest::Assertion for public method + assert_raises(Minitest::Assertion) { m.foo } end assert_passed(test_result) end @@ -168,7 +168,7 @@ def foo; end assert_passed(test_result) end - def test_mock_which_responds_like_object_with_protected_method_raises_no_method_error_when_method_is_stubbed + def test_mock_which_responds_like_object_with_protected_method_does_not_raise_exception_when_method_is_stubbed object = Class.new do def foo; end protected :foo @@ -176,7 +176,7 @@ def foo; end test_result = run_as_test do m = mock.responds_like(object) m.stubs(:foo) - assert_raises(NoMethodError) { m.foo } # vs no exception for public method + assert_nil m.foo end assert_passed(test_result) end @@ -196,14 +196,14 @@ def foo; end assert_passed(test_result) end - def test_mock_which_responds_like_object_with_private_method_raises_no_method_error_when_method_is_not_stubbed + def test_mock_which_responds_like_object_with_private_method_raises_unexpected_invocation_exception_when_method_is_not_stubbed object = Class.new do def foo; end private :foo end.new test_result = run_as_test do m = mock.responds_like(object) - assert_raises(NoMethodError) { m.foo } # vs Minitest::Assertion for public method + assert_raises(Minitest::Assertion) { m.foo } end assert_passed(test_result) end @@ -234,7 +234,7 @@ def foo; end assert_passed(test_result) end - def test_mock_which_responds_like_object_with_private_method_raises_no_method_error_when_method_is_stubbed + def test_mock_which_responds_like_object_with_private_method_does_not_raise_exception_when_method_is_stubbed object = Class.new do def foo; end private :foo @@ -242,7 +242,7 @@ def foo; end test_result = run_as_test do m = mock.responds_like(object) m.stubs(:foo) - assert_raises(NoMethodError) { m.foo } # vs no exception for public method + assert_nil m.foo end assert_passed(test_result) end