Skip to content

Commit

Permalink
Fix stubbing prepended only methods
Browse files Browse the repository at this point in the history
Previously, we're assuming the method must be defined in the
singleton class. However this is not always true. Whenever the
method was only defined in the prepended module, then it's not
defined in the singleton class. We need to find the owner of
the method instead, which is the prepended module.

Closes #1213
  • Loading branch information
godfat committed Apr 10, 2018
1 parent bbb50c9 commit 610871a
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 3 deletions.
13 changes: 10 additions & 3 deletions lib/rspec/mocks/method_double.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ def object_singleton_class
class << @object; self; end
end

# @private
def method_owner
if Object.instance_method(:respond_to?).bind(object).call(@method_name, true)
Object.instance_method(:method).bind(object).call(@method_name).owner
end
end

# @private
def configure_method
@original_visibility = visibility
Expand Down Expand Up @@ -102,10 +109,10 @@ def show_frozen_warning

# @private
def restore_original_visibility
return unless @original_visibility &&
MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name)
return unless method_owner && @original_visibility &&
MethodReference.method_defined_at_any_visibility?(method_owner, @method_name)

object_singleton_class.__send__(@original_visibility, method_name)
method_owner.__send__(@original_visibility, @method_name)
end

# @private
Expand Down
13 changes: 13 additions & 0 deletions spec/rspec/mocks/stub_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ module ToBePrepended
def value
"#{super}_prepended".to_sym
end

def value_without_super
:prepended
end
end

it "handles stubbing prepended methods" do
Expand Down Expand Up @@ -154,6 +158,15 @@ def object.value; :original; end
expect(object.value).to eq :stubbed
end

it "handles stubbing prepended methods without originally defined on an object's singleton class" do
object = Object.new
object.singleton_class.send(:prepend, ToBePrepended)

expect(object.value_without_super).to eq :prepended
allow(object).to receive(:value_without_super) { :stubbed }
expect(object.value_without_super).to eq :stubbed
end

it 'does not unnecessarily prepend a module when the prepended module does not override the stubbed method' do
object = Object.new
def object.value; :original; end
Expand Down

0 comments on commit 610871a

Please sign in to comment.