在 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_of
和allow_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
,但用于消除特定期望。它可能位于此处(请注意此文件中stub
和unstub
的定义):
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() 的功能?