从间谍调用原始方法

Call original method from spy

我有一个服务对象,看起来像这样:

class Service
  def call
    do_something if anything
  end

  private

  def do_something
    # do something
  end
end

我想测试 do_something 在某些情况下被调用。

我试过这样写:

RSpec.describe Service do
  it 'calls do_something' do
    service = instance_double(Service)
    service.stub(:do_something)
    expect(Service).to receive(:new).and_return service
    Service.new.call

    expect(service).to have_received(:do_something)
  end
end

显然这不起作用,因为我没有为 service 存根 call 方法。但是,如果我使用 service = instance_double('Service', call: true) 它不会调用 call,因此永远不会调用 do_something

我正在寻找类似 allow(service).to receive(:call).and_call_original 的东西(如果可能的话)(也许将 instance_double 替换为 object_double,这在这种情况下更有意义。有这样的东西吗?

如您所见,and_call_original 仅适用于部分双打 因此,如文档所述,尝试将它与其他类型的双打一起使用是不可能的。

如果我们仔细观察部分双倍 docs,我们可以看到 partial double 被描述为:

A partial test double is an extension of a real object in a system that is instrumented with
test-double like behavior in the context of a test. This technique is very common in Ruby
because we often see class objects acting as global namespaces for methods. For example,
in Rails:

person = double("person")
allow(Person).to receive(:find) { person }
In this case we're instrumenting Person to return the person object we've defined whenever
it receives the find message. We can also set a message expectation so that the example
fails if find is not called:

person = double("person")
expect(Person).to receive(:find) { person }
RSpec replaces the method we're stubbing or mocking with its own test-double like method.
At the end of the example, RSpec verifies any message expectations, and then restores the
original methods.

Note: we recommend enabling the verify_partial_doubles config option.

上面他们说:A partial test double is an extension of a real object 这意味着执行 expect(Service.new).to receive(:call).and_call_original 将是有效的,因为我们在部分双精度上创建它,该双精度最初是创建的真实世界对象,并且对其设置期望使其成为部分双精度。 但是如果我们这样做 expect(object_double(Service.new)).to receive(:call).and_call_original 那将不起作用,因为我们将这个期望设置在一个纯双精度上,它是一个验证双精度,而不是现实世界对象的扩展。这意味着在部分替身中,我们直接处理真实世界或真实系统对象,但在纯替身情况下,我们正在与替身交谈。这是基于文档类比,它适用于其他类型的双打,而不仅仅是 object_double.

如果您在 and_call_original 方法的文档中检查这个 link,您会注意到这个短语:

Use and_call_original to make a partial double response as it normally would

这是有道理的,因为它会像往常一样做出响应,并且通常在系统中我们会在真实对象上调用原始方法,或者 class 不会在双精度对象上调用。

[注意] 这里你要显式测试 do_something 方法,它是一个私有方法。就您而言,您只需要测试 public 方法,不应测试私有方法。因为如果使用私有方法的 public 方法成功运行,那肯定意味着私有方法按预期工作。

因此,为了确保训练您的 do_something 私有方法,您需要创建两个上下文:

context 'when anything is true' do
  tests for call method goes here in this case
end

context 'when anything is false' do
  tests for call method goes here
end

这意味着如果 anything 例如是一个执行 model.save 的模型,请确保在保存方法上设置期望,因此在一个上下文中它 returns false导致 do_method does 没有被执行。在另一个上下文中 when true 确保你设置了一个期望让它成为 return true 所以你的 do_something 方法被调用和执行这隐含地意味着它得到训练并且如果调用在这种情况下,测试通过肯定意味着 do_something 正在按预期工作。 要了解有关测试私有方法的更多信息,您可以观看 Sandi Matz 的精彩 talk