43

在编写 Rspec 测试时,我经常对should_receive. 我想知道是否有不那么侵入性的选择。

例如:

describe "making a cake" do
  it "should use some other methods" do
    @baker.should_receive(:make_batter)
    @baker.make_cake
  end
end

调用should_receive是一个很好的描述,但它破坏了我的代码,因为should_receive通过屏蔽原始方法来工作,并且除非实际返回一些击球手make_cake,否则无法继续。make_batter所以我把它改成这样:

@baker.should_receive(:make_batter).and_return(@batter)

这很丑,因为:

  • 看起来我正在测试make_batter是否正确返回,但实际上@batter我是在强制伪造版本的返回。make_batter
  • 它迫使我单独设置@batter
  • 如果make_batter有任何重要的副作用(我想可能是代码味道),我也必须让这些发生。

我希望这should_receive(:make_batter)将验证方法调用并将其传递给原始方法。如果我想存根它的行为以进行更好的隔离测试,我会明确地这样做:@baker.stub(:make_batter).and_return(@batter).

should_receive有没有办法在不阻止原始方法调用的情况下做类似的事情?我的问题是糟糕设计的征兆吗?

4

3 回答 3

63

看起来更好的 API 委托给 Myron Marston 提到的原始方法实际上已添加到rspec-mocks v2.12.0

因此,现在您可以在“想要设置消息期望而不干扰对象如何响应消息”的任何时候简单地执行此操作:

@baker.should_receive(:make_batter).and_call_original

感谢您添加此内容,迈伦。

于 2013-02-04T19:12:28.650 回答
11

您可以should_receive像这样运行原始方法:

@baker.should_receive(:make_batter, &@baker.method(:make_batter))

两者都should_receive支持stub传递块实现(即在调用方法时进行评估)。&@baker.method(:make_batter)获取原始方法对象的句柄,并将其作为块实现传递。

FWIW,我们希望提供一个更好的 API 来委托给原始方法(请参阅此问题),但很难在不破坏向后兼容性的情况下添加该功能。

于 2012-08-28T14:16:42.853 回答
6

您遇到此问题是should_receive因为您正在测试该make_cake方法的实现细节。在编写测试时,您应该只关注行为而不是一系列内部方法调用。否则,稍后重构您的代码也会导致对所有测试的大量重构。

当你想单独测试你的类时,Mocks 和 Stubs 会派上用场。在编写单元测试时,您的测试对象应该与任何其他对象隔离。当您同时处理多个对象时,两者都充当替身。在这种情况下,您可以should_receive通过调用方法来确保您的测试主体正确地将某些任务委托给另一个对象。

于 2012-08-28T13:56:35.147 回答