在 RSpec 中,是不是有等效于“unstub”但用于“should_receive”的方法?

Posted

技术标签:

【中文标题】在 RSpec 中,是不是有等效于“unstub”但用于“should_receive”的方法?【英文标题】:In RSpec, is there a method equivalent to "unstub" but for "should_receive"?在 RSpec 中,是否有等效于“unstub”但用于“should_receive”的方法? 【发布时间】:2012-06-06 19:15:38 【问题描述】:

在使用 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”。

那么,除了“should_receive”之外,是否有任何等效于“unstub”的方法?

帕纳约蒂斯

【问题讨论】:

你不能编辑少于6个字符的东西太荒谬了!你上面的方法名称是mocking_resest,应该是mocking_reset 添加编辑消息,您的编辑将超过 6 个字符 :) 【参考方案1】:

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

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

或者如果不是任何期望,只是一个简单的存根:

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

记住还有expect_any_instance_ofallow_any_instance_of

【讨论】:

这不等同于unstub,因为它会继续观察对模拟方法的调用。【参考方案2】:

目前 (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 或之前 :all 块),或者如果你的例子试图做太多事情(即,在一个示例中有多个断言)。

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

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

【讨论】:

非常感谢您的回答。我想要这个功能的原因是因为我在“每个”之后执行了一个“clean_test_data”方法。这需要使用真正的方法。在 SOF 上发布我的问题后,我意识到我仍然可以使用“clean_test_data”但在“之前:每个”上,这就是我的问题的解决方案。这样,我终于不需要任何 unstub 或 unmock 了。同时,由于我过去找到了使用“unstub”的充分理由,那么出于完整性的原因,我不明白为什么没有等效的“unset_expectation”,然后让模拟用户决定。【参考方案3】:

对于 RSpec >= 2.14,接受的答案将不再有效。

我向您推荐 Bernhard Köhler 在此线程上的回答(转载如下以防止链接失效):undefined method `rspec_reset' with rspec version 2.14

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

您可以通过以下方式取消您的 expect/allow 调用:

RSpec::Mocks.proxy_for(object).reset

对于RSpec 3.9.0,至少,我发现上面已经被替换为:

RSpec::Mocks.space.proxy_for(object).reset

【讨论】:

谢谢,非常感谢您添加 >3.9.0 特定代码。

以上是关于在 RSpec 中,是不是有等效于“unstub”但用于“should_receive”的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Dojo 是不是有等效于 jQuery.trigger() 的功能?

Pyspark 中是不是有等效于 SQL 的 MSCK REPAIR TABLE 的方法

msbuild 中是不是有等效于 make -k 的选项?

R中是不是有等效于“count if”函数的功能?

MS-SQL 中是不是有等效于 SHA1() 的方法?

PostgreSql中是不是有等效于sp_getapplock,sp_releaseapplock的?