当一个对象的构造函数生成另一个对象时存根



所以我有一些代码,经过粗略简化,看起来像这样:

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

相关内容

  • 没有找到相关文章

最新更新