1

using Mocha in simple code turned in unexpected way. Could you explain what is going wrong?

require 'test-unit'
require 'mocha'

class A
  def m
    caller.first
  end
end

So using this simple class, we can get the latest caller:

A.new.m #=> "(irb):32:in `irb_binding'" (for example)

But if I want to stub caller call, things going wrong.

a = A.new
a.stubs(:caller)

Mocha::ExpectationError: unexpected invocation: #<A:0x6aac20>.caller()

My guess is to check out Mocha sources, but I will do it later ;)


  • ruby 1.9.3p194
  • test-unit 2.5.0, 2.4.8
  • mocha 0.12.6
Vega
  • 27,856
  • 27
  • 95
  • 103
Mark Huk
  • 2,379
  • 21
  • 28

3 Answers3

1

This is a partial explanation, but I hope it is still useful.

As you've suggested, a way to understand what's going on here is to check the Mocha sources. I think the key to the issue is that the Expectation class, which is used when creating the stub, makes use of the caller method itself.

A workaround would be to use alias_method e.g.

class A
  alias_method :my_caller, :caller # allow caller to be stubbed

  def m
    my_caller.first
  end
end

a = A.new
a.stubs(:my_caller)
mikej
  • 65,295
  • 17
  • 152
  • 131
  • Thank you. So as note, basically there is no way of stubbing `caller` in `Mocha` by now.. – Mark Huk Oct 18 '12 at 10:44
  • It seems that way. I think it's a consequence of the fact that Mocha wants to use the nicer style `a.stubs(:method)` rather than something like `stub(a, :method)` which means that the code that sets up the stub runs in the context of your object so you'll run into this kind of problem if you try and stub any method used in the stub creation process even though you really only want the behaviour stubbed for your object. Obviously, the chosen Mocha syntax is nicer overall, it just causes problems with edge cases like this. – mikej Oct 18 '12 at 10:51
  • Seems like issue for me. As `Kernel` is included in `Object`, `Mocha` could call `caller` on `Kernel` or somewhat like this. – Mark Huk Oct 18 '12 at 18:54
1

How about this?

allow_any_instance_of(Kernel)
  .to receive(:caller)
  .and_return(["anything_you_want.rb"])
alex
  • 1,042
  • 4
  • 18
  • 33
0

You are calling caller from m. So caller.first will always be the line that calls m, which is probably useless. Probably what you wanted is caller[1], not caller.first (or caller[0]).

sawa
  • 165,429
  • 45
  • 277
  • 381