让RSpec验证方法是否接收特定块

时间:2013-05-28 14:02:33

标签: ruby rspec

如何使用RSpec验证方法是否收到特定块?考虑这个简化的例子:

class MyTest
  def self.apply_all_blocks(collection, target)
    collection.blocks.each do |block|
      target.use_block(&block)
    end
  end
end

我想要一个规范,验证target.use_block返回的每个块都会调用collection.blocks

以下代码不起作用:

describe "MyTest" do
  describe ".apply_all_blocks" do
    it "applies each block in the collection" do
      target = double(Object)
      target.stub(:use_block)

      collection = double(Object)
      collection.stub(:blocks).and_return([:a, :b, :c])

      target.should_receive(:use_block).with(:a)
      target.should_receive(:use_block).with(:b)
      target.should_receive(:use_block).with(:c)

      MyTest.apply_all_blocks(collection, target)
    end
  end
end

(另外,use_block不一定会调用该块,因此仅测试该块接收call是不够的。同样,我不认为target.should_receive(:use_block).and_yield会做我所做的事情想。)

2 个答案:

答案 0 :(得分:3)

如果您创建lambda而不是符号,它将按预期工作:

describe "MyTest" do
  describe ".apply_all_blocks" do
    let(:a) { lambda {} }
    let(:b) { lambda {} }
    let(:c) { lambda {} }
    it "applies each block in the collection" do
      target = double(Object)
      target.stub(:use_block)

      collection = double(Object)
      collection.stub(:blocks).and_return([a, b, c])

      target.should_receive(:use_block).with(&a)
      target.should_receive(:use_block).with(&b)
      target.should_receive(:use_block).with(&c)

      MyTest.apply_all_blocks(collection, target)
    end
  end
end

注意:我将班级名称从Test更改为MyTest,因此它实际上会运行; Test将与真正的Test类发生冲突。我也修改了你的问题,以便它可以剪切并粘贴。

答案 1 :(得分:1)

我知道这是一个老问题,但目前接受的答案是错误的。

接收验证特定块的正确方法是将验证块传递给should_receive,专门将收到的块与您希望接收的块进行比较:

在RSpec 2.13中(原始问题时的当前时间):

a = lambda {}
target.should_receive(:use_block) do |&block|
  expect(block).to eq(a)
end

在RSpec 3.4中(撰写本文时的当前内容):

a = lambda {}
expect(target).to receive(:use_block) do |&block|
  expect(block).to eq(a)
end

如另一个答案中所建议的那样将块传递给with不足以验证该块是否实际被接收,因为rspec使用该块来设置返回值,而不是与块实际比较该块接收。

请参阅documentation on passing blocksreceive

另见recent discussion on RSpec mailing list