11

In a unit test I need to test whether alias methods defined by alias_method have been properly defined. I could simply use the same tests on the aliases used for their originals, but I'm wondering whether there's a more definitive or efficient solution. For instance, is there a way to 1) dereference a method alias and return its original's name, 2) get and compare some kind of underlying method identifier or address, or 3) get and compare method definitions? For example:

class MyClass
  def foo
    # do something
  end

  alias_method :bar, :foo
end

describe MyClass do
  it "method bar should be an alias for method foo" do
    m = MyClass.new
    # ??? identity(m.bar).should == identity(m.foo) ???
  end
end

Suggestions?

Mori
  • 27,279
  • 10
  • 68
  • 73
  • possible duplicate of [Is it possible to identify aliased methods in Ruby?](http://stackoverflow.com/questions/3676834/is-it-possible-to-identify-aliased-methods-in-ruby) – Marc-André Lafortune Oct 06 '10 at 11:43

3 Answers3

18

According to the documentation for Method,

Two method objects are equal if that are bound to the same object and contain the same body.

Calling Object#method and comparing the Method objects that it returns will verify that the methods are equivalent:

m.method(:bar) == m.method(:foo)
bk1e
  • 23,871
  • 6
  • 54
  • 65
  • I was sure I remembered that this didn't work, but I just tried it to confirm and it works consistently in Ruby 1.8, 1.9 and MacRuby. But I still don't see it in RubySpec, so it very likely won't work on an unrelated implementation. – Chuck Oct 06 '10 at 05:57
  • 2
    Also, in general, methods that merely have identical bodies but aren't copied one from the other are emphatically *not* equal. Proof: `Class.new{def foo() end; def bar() end; puts instance_method(:foo)==instance_method(:bar)}` – Chuck Oct 06 '10 at 06:04
  • @Chuck: Thanks for pointing that out. I should have tried it. – bk1e Oct 06 '10 at 06:11
  • 2
    Also, "returns true on aliased methods" from http://github.com/rubyspec/rubyspec/blob/master/core/method/equal_value_spec.rb + http://github.com/rubyspec/rubyspec/blob/master/core/method/shared/eql.rb seems to cover `m.method(:bar) == m.method(:foo)` when one is an alias of the other. – bk1e Oct 06 '10 at 06:25
3

bk1e's method works most of the time, but I just happened to hit the case where it doesn't work:

class Stream
  class << self
    alias_method :open, :new
  end
end

open = Stream.method(:open)
new = Stream.method(:new)
p open, new                   # => #<Method: Stream.new>, #<Method: Class#new>
p open.receiver, new.receiver # => Stream, Stream
p open == new                 # => false

The output is produced in Ruby 1.9, not sure if it's a bug or not since Ruby 1.8 produces true for the last line. So, if you are using 1.9, be careful if you are aliasing an inherited class method (like Class#new), These two methods are bound to the same object (the class object Stream), but they are considered not equivalent by Ruby 1.9.

My workaround is simple - alias the original method again and test the equality of the two aliases:

class << Stream; alias_method :alias_test_open, :new; end
open = Stream.method(:open)
alias_test_open = Stream.method(:alias_test_open)
p open, alias_test_open                   # => #<Method: Stream.new>, #<Method: Stream.new>
p open.receiver, alias_test_open.receiver # => Stream, Stream
p open == alias_test_open                 # => true

Hope this helps.

UPDATE:

See http://bugs.ruby-lang.org/issues/7613

So Method#== should return false in this case since a super call would invoke different methods; it is not a bug.

Su Zhang
  • 2,155
  • 2
  • 14
  • 11
1

Calling MyClass.instance_method(:foo) will result UnboundMethod instance, which has eql? method.

So the answer is:

describe MyClass do
  subject { described_class }

  specify do
    expect(subject.instance_method(:foo)).to be_eql(subject.instance_method(:bar))
  end
end
denis.peplin
  • 9,585
  • 3
  • 48
  • 55