Rspec:访问Klass.any_instance.stub块中的实例

时间:2011-05-31 21:57:56

标签: ruby rspec tdd bdd

Feature: test randomness
    In order to make some code testable
    As a developer
    I want Array#sample to become Array#first

如果可以访问Klass.any_instance.stub块中的实例,那么这是可能的。像这样:

Array.any_instance.stub(:sample) { instance.first }

但是那个afaik是不可能的。

无论如何,想要的场景!

2 个答案:

答案 0 :(得分:2)

我找到了一个hacky解决方案,我在rspec版本2.13.1和2.14.4上测试过。你需要binding_of_caller gem。

Helper方法 - 这应该可以通过rspec示例调用:

# 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

然后在你的例子中:

Array.any_instance.stub(:sample) do
  instance = any_instance_receiver
  instance.first
end

我已经对堆栈搜索设置了限制,以避免搜索大量堆栈。我不明白为什么你需要增加它,因为它应该总是在cur_idx == 8左右。

请注意,生产中可能不建议使用binding_of_caller

答案 1 :(得分:0)

对于那些现在遇到困难的人来说,Rspec 3通过传递给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 }

我发现了here