Skip to content

Commit

Permalink
Merge pull request #207 from rspec/handle_aliased_methods
Browse files Browse the repository at this point in the history
Stash and restore aliased methods when the owner is reported wrong.
  • Loading branch information
alindeman committed Dec 20, 2012
2 parents 63ae97f + f86d02c commit ac90673
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 3 deletions.
21 changes: 18 additions & 3 deletions lib/rspec/mocks/instance_method_stasher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,29 @@ def method_defined_directly_on_klass?
end

# @private
def method_defined_on_klass?
@klass.method_defined?(@method) || @klass.private_method_defined?(@method)
def method_defined_on_klass?(klass = @klass)
klass.method_defined?(@method) || klass.private_method_defined?(@method)
end

if ::UnboundMethod.method_defined?(:owner)
# @private
def method_owned_by_klass?
@klass.instance_method(@method).owner == @klass
owner = @klass.instance_method(@method).owner
# On 1.8 (and some 1.9s -- e.g. rubinius) aliased methods
# can report the wrong owner. Example:
# class MyClass
# class << self
# alias alternate_new new
# end
# end
#
# MyClass.owner(:alternate_new) returns `Class` on 1.8,
# but we need to consider the owner to be `MyClass` because
# it is not actually available on `Class` but is on `MyClass`.
# Hence, we verify that the owner actually has the method defined.
# If the given owner does not have the method defined, we assume
# that the method is actually owned by @klass.
owner == @klass || !(method_defined_on_klass?(owner))
end
else
# @private
Expand Down
11 changes: 11 additions & 0 deletions spec/rspec/mocks/and_call_original_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,17 @@ def instance.foo; :bar; end
expect(inst.meth_1).to eq(:original)
end

it 'works for aliased methods' do
klass = Class.new do
class << self
alias alternate_new new
end
end

klass.should_receive(:alternate_new).and_call_original
expect(klass.alternate_new).to be_an_instance_of(klass)
end

context 'on an object that defines method_missing' do
before do
klass.class_eval do
Expand Down
27 changes: 27 additions & 0 deletions spec/rspec/mocks/stash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def self.foo(arg)
end
end
end

it "keeps the original method intact after multiple expectations are added on the same method" do
klass.should_receive(:foo).with(:fizbaz).and_return(:wowwow)
klass.should_receive(:foo).with(:bazbar).and_return(:okay)
Expand All @@ -23,5 +24,31 @@ def self.foo(arg)
klass.foo(:yeah).should equal(:original_value)
end
end

describe "when a class method is aliased on a subclass and the method is mocked" do
let(:klass) do
Class.new do
class << self
alias alternate_new new
end
end
end

it "restores the original aliased public method" do
klass = Class.new do
class << self
alias alternate_new new
end
end

klass.should_receive(:alternate_new)
expect(klass.alternate_new).to be_nil

klass.rspec_verify

klass.rspec_reset
expect(klass.alternate_new).to be_an_instance_of(klass)
end
end
end
end

0 comments on commit ac90673

Please sign in to comment.