【问题标题】:Stubbing when an object's constructor builds another object当一个对象的构造函数构建另一个对象时存根
【发布时间】:2012-07-06 13:14:57
【问题描述】:

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

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 的测试应该 stub B 中的任何方法,并且需要大量存根意味着你的代码一开始就不好。)

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

有什么想法吗?

【问题讨论】:

    标签: ruby constructor rspec stubbing rr


    【解决方案1】:

    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)
    

    【讨论】:

      【解决方案2】:

      RR 版本的 Marc-Andre 的回答:

      before do
        stub(B).new { @b }
      end
      
      it 'uses the correct options' do
        B.should have_received.new(hash_including(:foo => 'whatever'))
      end
      

      【讨论】:

        【解决方案3】:

        你可以写像B.should_receive(:new).with({:foo => 'whatever'})这样的东西。

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

        【讨论】:

        • 天啊!谢谢,不敢相信我之前没有看到这种方式。添加 RR 版本作为单独的答案。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-03-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-15
        • 2015-10-29
        • 1970-01-01
        相关资源
        最近更新 更多