如何重置模拟类方法的期望?

时间:2014-08-14 13:00:31

标签: ruby unit-testing rspec mocking

很抱歉,如果这很简单。我是ruby以及rspec的新手,似乎rspec是一个非常“模糊”的版本。世界(特别是来自.net背景时)。

在我的规范'中,我有:

before(:each) do
    expect(File).to receive(:exist?).with("dummy.yaml").and_return (true)
end

这适用于我的所有'示例',除了我希望它返回false的一个。

expect(File).to receive(:exist?).with("non_existent.yaml").and_return (false)

这显然无法通过我的测试,因为虽然" non_existent.yaml"期望得到满足," dummy.yaml"不是:

(<File (class)>).exist?("dummy.yaml")
       expected: 1 time with arguments: ("dummy.yaml")
       received: 0 times

那么我怎样才能重置&#39; on&#39; File.exist?&#39; (一个类方法模拟)在我设置它的新期望之前? (...&#34; non_existent.yaml&#34; ..)

我用谷歌搜索了它:

RSpec::Mocks.proxy_for(your_object).reset

但是这给了我:

NoMethodError:
   undefined method `proxy_for' for RSpec::Mocks:Module

4 个答案:

答案 0 :(得分:6)

我在文档中的任何地方都找不到这是你应该怎么做的,过去的行为表明这个解决方案将来也可能会发生变化,但显然这就是你现在可以做到的:

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

我会关注@ BroiSatse的评论,并考虑重新设计测试,旨在将期望从before块移开。正如你所说,before块用于设置,设置是一个非常奇怪的地方,可以达到预期目的。

我不确定你是如何选择这种设计的,但我可以提出两种可能的选择:

  • 如果测试是微不足道的,并且无论如何都会有效,那么你应该用这个明确的期望创建一个测试,同时将其存入其他测试:

    before(:each) do
      allow(File).to receive(:exist?).with("dummy.yaml").and_return (true)
    end
    
    it "asks if file exists" do
      expect(File).to receive(:exist?).with("dummy.yaml").and_return (true)
      # do the test...
    end
    
  • 如果期望应该针对每个测试运行,因为每个方案中的更改是上下文,您应该考虑使用shared examples

    shared_examples "looking for dummy.yaml" do 
      it "asks if file exists" do
        expect(File).to receive(:exist?).with("dummy.yaml").and_return (true)
        # do the test...
      end
    end
    
    it_behaves_like "looking for dummy.yaml" do 
      let(:scenario) { "something which sets the context"}
    end
    

如果有更多推荐/记录的解决方案来重置模拟对象,您可能还需要ask myron ...

答案 1 :(得分:0)

这对我来说是从类中取消特定方法的工作:

mock = RSpec::Mocks.space.proxy_for(MyClass)
mock.instance_variable_get(:@method_doubles)[:my_method].reset

注意:的逻辑相同 RSpec::Mocks.space.proxy_for(MyClass).reset重置所有方法

答案 2 :(得分:0)

@Uri Agassi's answer和我answered on another similar question的基础上进行扩展,我发现我可以使用RSpec::Mocks.space.registered?来检查某个方法是否为模拟方法,并可以使用RSpec::Mocks.space.proxy_for(my_mocked_var).reset来重置其值。

这里是the example I included in my other answer

  

示例:重置模拟值

     

例如,如果我们想reset将此模拟恢复为未模拟   默认值,我们可以使用RSpec::Mocks.space.proxy_for帮助程序   找到我们的模拟,然后reset

# when
#   Rails.configuration.action_controller.allow_forgery_protection == false
# and
#   allow(Rails.configuration.action_controller).to receive(:allow_forgery_protection).and_return(true)

RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true

Rails.configuration.action_controller.allow_forgery_protection
# => true

RSpec::Mocks.space.proxy_for(Rails.configuration.action_controller).reset

Rails.configuration.action_controller.allow_forgery_protection
# => false
     

但是请注意,即使已重置模拟值,   模拟遗物registered?

RSpec::Mocks.space.registered?(Rails.configuration.action_controller)
# => true

答案 3 :(得分:0)

使用“expect_any_instance”时,我使用以下方法成功更改了模拟(例如,我们的示例:发布 Twitter 帖子并返回不同的推文 ID)

expect_any_instance_of(Twitter::REST::Client).to receive(:update).and_return(Hashie::Mash.new(id: "12"))
# post tweet

RSpec::Mocks.space.verify_all
RSpec::Mocks.space.reset_all

expect_any_instance_of(Twitter::REST::Client).to receive(:update).and_return(Hashie::Mash.new(id: "12346"))
# post another tweet