Browse files

Stash and restore aliased methods when the owner is reported wrong.

Fixes #206.

While I was at it, I wanted to verify that `and_call_original` works
with this case, so I added a spec for it, too.
  • Loading branch information...
1 parent 63ae97f commit f86d02c9b067a72775fdedb06f22d87e5459ffdb @myronmarston myronmarston committed Dec 20, 2012
View
21 lib/rspec/mocks/instance_method_stasher.rb
@@ -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
View
11 spec/rspec/mocks/and_call_original_spec.rb
@@ -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
View
27 spec/rspec/mocks/stash_spec.rb
@@ -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)
@@ -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 f86d02c

Please sign in to comment.