如何使用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
会做我所做的事情想。)
答案 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 blocks至receive
。