在RSpec中,是否有一种方法相当于“unstub”而是“should_receive”?

时间:2012-06-01 14:25:50

标签: rspec mocking stub

使用RSpec时是否有任何方法可以删除任何存根和模拟?

示例:

RestClient.should_receive(:delete).with("http://www.example.com")
...
... 

# this will remove the mocking of "should_receive" and 
# restore the proper "delete" method on "RestClient".
RestClient.mocking_reset

mocking_reset是我所需功能的虚构名称)。

我知道有一个方法“unstub”可以重置“stubs”而不是“should_receive”。

那么,是否有任何方法等同于“unstub”而是“should_receive”?

Panayotis

3 个答案:

答案 0 :(得分:39)

您可以通过以下方式覆盖之前的一些模拟:

expect(RestClient).to receive(:delete).and_call_original

或者,如果没有任何期望,只需一个简单的存根:

allow(RestClient).to receive(:delete).and_call_original

请记住,还存在expect_any_instance_ofallow_any_instance_of

答案 1 :(得分:31)

目前(rspec-mocks 2.10.1),没有一种方法等同于unstub,而是should_receive。您可以使用rspec_reset重置所有存根和模拟,也可以编写脏的黑客来删除特定的期望(我不建议这样做)。

以下是删除对象的所有存根和期望的示例:

describe "resetting stubs and expectations with rspec_reset" do
  before do
    @person = mock('person')
    @person.should_receive(:poke)
  end

  it "should not fail when we reset all stubs and expectations" do
    @person.rspec_reset
  end
end

请注意,此方法在rspec源代码中注释为@private,这意味着您应该避免使用它超出绝对必要的范围,并且在未经警告的情况下可能会在未来版本的rspec中中断。然而,它在rspec本身的规范中被广泛使用,所以它似乎不太可能很快被弃用。

在深入研究一下rspec-mocks代码之后,你当然可以做一些非常讨厌的事情并自己撕掉一个特定的期望:

# Works in rspec 2.10.1
describe "removing an expectation with an ugly hack" do
  before do
    @person = mock('person')
    @person.should_receive(:poke)
  end

  it "should not fail after we hack rspec by violating every law of good programming, ever" do
    @person.instance_variable_get(:@mock_proxy).instance_variable_get(:@method_double)[:poke].clear
  end
end

这非常糟糕,因为它违反了rspec测试包的封装,你不应该这样做。相反,如果你真的有一个令人信服的理由去除一个特定的期望,那么正确的做法是在rspec-mocks上游添加一个公共方法,为unstub添加一个并行方法,但是为了消除特定的期望。它可能位于此处(请注意此文件中stubunstub的定义):

https://github.com/rspec/rspec-mocks/blob/master/lib/rspec/mocks/methods.rb

在外出并使用上述任何建议消除期望之前,您可能需要考虑您的规格是否真的需要这样做或者是否应该重构。使用should_receive是关于代码的断言,通常你应该尝试创建只断言一件事的例子(即it块)。我会非常好奇为什么你需要rspec_reset这样的东西,除非你试图在全局设置中做太多(例如before :each或之前:所有块),或者如果你的例子正在尝试做太多(即在一个例子中有多个断言)。

对此的一个例外当然可能是在rspec-mocks的测试套件中,其中rspec_reset用于在测试应用存根和模拟的功能的示例之间重置状态。除非你这样做,否则很可能你的测试可以改进,不依赖于对象上的存根和模拟的全局重置。

我希望这会有所帮助,如果您认为有一个合法的案例可以向unstub添加等效方法但是对于消息期望(即,在使用should_receive之后),请告诉我。 。如果我确信这是一件好事,我会考虑添加这种方法,并建议将其添加到上游。也许它会被称为unset_expectation?也请随意使用上面的代码和内部结构的指南,自己组合一个rspec-mocks的拉取请求,我们将看看它是否被接受创建一个等同于unstub的模拟。

答案 2 :(得分:7)

对于RSpec> = 2.14,接受的答案将不再有效。我请你参考BernhardKöhler在这个主题上的答案(转载如下以防止链接腐烂):undefined method `rspec_reset' with rspec version 2.14

  

RSpec 2.14中不存在重置方法。相反,它是一个帮手   rspec-mocks的spec_helper.rb文件中定义的方法   项目

module VerifyAndResetHelpers
  def verify(object)
    RSpec::Mocks.proxy_for(object).verify
  end

  def reset(object)
    RSpec::Mocks.proxy_for(object).reset
  end
end
     

您可以看到此方法将重置操作委托给   底层代理而不是将其添加到类的定义上   有问题的对象。