rspec any_instance存根不会重新存储旧实例

时间:2013-08-07 00:13:45

标签: ruby-on-rails ruby rspec

我最近在Ruby 2.0,Rails 4和Rspec 2.13.1中发现了这种奇特的行为

当我使用ClassName.any_instance.stub(:method_name)存根实例方法时,它正确存根并过去我创建的实例。但是,当我通过更改返回值重新生成它时,旧实例返回旧的存根值,而不是更新的存根值。

例如,我有这个虚拟类定义

class A
  def test(x)
    return x
  end
end

并且此测试描述了行为:

it 'strange stubbing behavior' do
  inst = A.new
  inst.test(1).should eq 1                         #passes
  A.any_instance.stub(:test).and_return(10)
  inst.test(0).should eq 10

  A.any_instance.unstub(:test)                     #has no effect
  A.any_instance.stub(:test).and_return(100)

  inst.test(0).should eq 100                       #expects 100, got 10

  A.any_instance.stub(:test) do |a|
    a + 2
  end
  inst.test(3).should eq 5   #also fails           # also got 10
end

为什么rspec的行为如此?它是否定义了行为?如果是这样,那么重新设置旧实例的正确方法是什么。或者这是一个错误?

编辑:在其他人提出“质疑问题”答案之前,我想指出我通过重新思考规范并重新组织它来解决我原来的问题。但是,我仍然很好奇为什么RSpec会这样做

2 个答案:

答案 0 :(得分:1)

在我看来,存根方法并不是你想要的。

一般来说,如果我想真正存根,那么它实际上只需要返回我想要的1值。我从来没有发现自己试图" restub"就像你上面做的那样。

也就是说,有些方法可以让你的模拟在不同的调用中返回不同的值,这可能是你在这里尝试做的所有事情。在rspec中,您可以通过将多个值作为参数传递给模拟调用来实现此目的。例如:

inst.stub(:test).and_return(1,2,3)
inst.test(0) #=> 1
inst.test(0) #=> 2
inst.test(0) #=> 3

答案 1 :(得分:1)

简而言之,当您在该实例上调用存根的情况中使用any_instance.unstub 时,RSpec似乎确实不会取消存储现有实例。同样,如您所发现的,使用any_instance.stub进行存根将无法与以前存根/调用的实例一起使用。

但是,如果您未调用或显式取消存储现有实例,则任何带有stub的新any_instance将在该实例上按预期工作。例如,您的示例的以下修改将起作用。

it 'strange stubbing behavior' do
  inst = A.new
  inst.test(1).should eq 1                         #passes
  A.any_instance.stub(:test).and_return(10)
  # SKIP THE INVOCATION SO TEST WILL PASS
  # inst.test(0).should eq 10

  A.any_instance.unstub(:test)                     #has no effect (on existing instances)

  A.any_instance.stub(:test).and_return(100)

  inst.test(0).should eq 100                       #expects 100, got 10

  inst.unstub(:test) # UNSTUB INSTANCE SO TEST WILL PASS                                                            
  A.any_instance.stub(:test) do |a|
    a + 2
  end
  inst.test(3).should eq 5   #also fails           # also got 10
end

不确定这是否是预期的行为,但是为此提交了issue。可在https://www.relishapp.com/rspec/rspec-mocks/docs/method-stubs/stub-on-any-instance-of-a-class#any-instance-unstub查看的RSpec Cucumber测试仅测试unstub是否适用于新实例。

(对@Theresa Luu求助于此。)