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

jpa*_*kal 5 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
Run Code Online (Sandbox Code Playgroud)

换句话说,当我使用选项初始化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'})
Run Code Online (Sandbox Code Playgroud)

但这有两个问题.

首先,我无法B使用实际选项实例化实际副本.如果我在块中调用B.new,它会调用存根版本并循环直到堆栈弹出.我可以@b = B.new在存根之前设置,但我不知道将要传递的选项,从而击败测试点.

(并且在有人打电话给我之前:是的,在严格的单元测试教条中,A的测试应该在B中删除任何方法,并且需要大量存根意味着你的代码首先是坏的.)

其次,should在测试设置中,而不是在it ... do ... end之后的单独块中,感觉是错误的.但由于我不能创造一个实际的B(见上文),我也无法真正询问它的建设后状态.

有任何想法吗?

Dav*_*les 9

should从语法马克-安德烈·Lafortune的答案出现在RSpec中3.下列被弃用expect的语法似乎不过工作,:

expect(B).to receive(:new).with(foo: 'whatever')
Run Code Online (Sandbox Code Playgroud)

请注意,如果要B.new返回特定实例(例如,测试双精度),可以使用and_return:

b = instance_double(B)
expect(B).to receive(:new).with(foo: 'whatever').and_return(b)
Run Code Online (Sandbox Code Playgroud)


Mar*_*une 3

你可以写类似的东西B.should_receive(:new).with({:foo => 'whatever'})

就我个人而言,我避免存根/嘲笑,而宁愿测试行为;事实上,B使用给定的一组选项创建一个新的选项是依赖于实现的,我不会直接测试它。