Rspec: access to instance inside Klass.any_instance.stub block
Feature: test randomness
In order to make some code testable
As a developer
I want Array#sample to become Array#first
It would be possible if one could access instance inside Klass.any_instance.stub block. Something like this:
Array开发者_JS百科.any_instance.stub(:sample) { instance.first }
But that afaik is not possible.
Anyway, scenarios wanted!
I found a hacky solution, which I've tested on rspec versions 2.13.1 and 2.14.4. You'll need the binding_of_caller
gem.
Helper method - this should be callable by your rspec example:
# must be called inside stubbed implementation
def any_instance_receiver(search_limit = 20)
stack_file_str = 'lib/rspec/mocks/any_instance/recorder.rb:'
found_instance = nil
# binding_of_caller's notion of the stack is different from caller(), so we need to search
(1 .. search_limit).each do |cur_idx|
frame_self, stack_loc = binding.of_caller(cur_idx).eval('[self, caller(0)[0]]')
if stack_loc.include?(stack_file_str)
found_instance = frame_self
break
end
end
raise "instance not found" unless found_instance
return found_instance
end
Then in your example:
Array.any_instance.stub(:sample) do
instance = any_instance_receiver
instance.first
end
I've set a limit on the stack searching, to avoid searching a huge stack. I don't see why you'd need to increase it, since it should always be around cur_idx == 8
.
Note that using binding_of_caller
is probably not recommended in production.
For those stumbling across this now, Rspec 3 implements this functionality via the first argument in the block passed to stub
:
RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks = true # I believe this is the default
Array.any_instance.stub(:sample) { |arr| arr.first }
I found this here.
精彩评论