我最近在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会这样做答案 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求助于此。)