Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rspec: access to instance inside Klass.any_instance.stub block

Tags:

tdd

ruby

rspec

bdd

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.any_instance.stub(:sample) { instance.first }

But that afaik is not possible.

Anyway, scenarios wanted!

like image 407
artemave Avatar asked Nov 05 '22 21:11

artemave


1 Answers

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.

like image 123
Kelvin Avatar answered Nov 09 '22 16:11

Kelvin