所以我有一些代码,经过粗略简化,看起来像这样:
class B
def initialize opts
@opts = opts
end
end
class A
def initialize opts
# defaults etc applied to opts
@b = B.new opts
end
end
换句话说,当我使用选项初始化 A 时,它会创建一个 B 并将一组修改后的选项传递给它。
我想测试一下B.new是否得到了正确的参数。 现在,我这样做,使用 RSpec/RR:
@b = Object.new
# stub methods on @b here
stub(B).new { |options|
options[:foo].should == 'whatever'
@b
}
A.new({:foo => 'whatever'})
但这有两个问题。
首先,我无法使用实际选项实例化B
的实际副本。 如果我在块内调用 B.new,它会调用存根版本并循环直到堆栈弹出。 我可以在存根之前设置@b = B.new
,但我不知道将传入的选项,从而击败了测试的重点。
(在有人叫我之前:是的,在严格的单元测试教条中,对 A 的测试应该存根掉 B 中的任何方法,并且需要存根很多意味着你的代码首先是坏的。
其次,将should
放在测试的设置中,而不是放在单独的it ... do ... end
块中,感觉是错误的。 但是由于我无法创建实际的B
(见上文),我也无法真正询问其构建后的状态。
有什么想法吗?
Marc-André Lafortune的答案中的should
语法似乎在RSpec 3中被弃用。但是,以下expect
语法似乎有效:
expect(B).to receive(:new).with(foo: 'whatever')
请注意,如果您希望B.new
返回特定实例(例如测试替身),则可以使用 and_return
:
b = instance_double(B)
expect(B).to receive(:new).with(foo: 'whatever').and_return(b)
你可以写一些类似B.should_receive(:new).with({:foo => 'whatever'})
的东西。
就个人而言,我避免存根/嘲笑,宁愿测试行为;使用一组给定的选项创建新B
的事实取决于实现,我不会直接测试它。
Marc-Andre 回答的 RR 版本:
before do
stub(B).new { @b }
end
it 'uses the correct options' do
B.should have_received.new(hash_including(:foo => 'whatever'))
end