当对象的构造函数构建另一个对象时进行存根

时间:2012-07-06 05:01:51

标签: ruby constructor rspec stubbing rr

所以我有一些代码,大致简化,看起来像这样:

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(见上文),我也无法真正询问其构建后的状态。

有什么想法吗?

3 个答案:

答案 0 :(得分:9)

来自Marc-André Lafortune's answershould语法似乎在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