Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implicitly set responder on partial mocks #532

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/mocha/class_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 8 additions & 3 deletions lib/mocha/mock.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -381,9 +382,13 @@ def raise_unexpected_invocation_error(invocation, matching_expectation)
end

def check_responder_responds_to(symbol)
if @responder && [email protected]_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 && [email protected]_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
Expand Down
2 changes: 1 addition & 1 deletion lib/mocha/object_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions lib/mocha/ruby_version.rb
Original file line number Diff line number Diff line change
@@ -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
16 changes: 8 additions & 8 deletions test/acceptance/responds_like_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -168,15 +168,15 @@ 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
end.new
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
Expand All @@ -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
Expand Down Expand Up @@ -234,15 +234,15 @@ 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
end.new
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
Expand Down