所以我有一些代码,大致简化,看起来像这样:
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
(见上文),我也无法真正询问其构建后的状态。
有什么想法吗?
答案 0 :(得分:9)
来自Marc-André Lafortune's answer的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)
答案 1 :(得分:3)
您可以编写类似B.should_receive(:new).with({:foo => 'whatever'})
的内容。
就个人而言,我避免抄写/嘲笑,宁愿测试行为;使用给定选项集创建新B
的事实取决于实现,我不会直接测试。
答案 2 :(得分:1)
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