Ruby-on-rails RSpec 存根方法可以按顺序返回不同的值吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5947999/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can RSpec stubbed method return different values in sequence?
提问by Mike Blyth
I have a model Family with a method locationwhich merges the locationoutputs of other objects, Members. (Members are associated with families, but that's not important here.)
我有一个模型家族,location它有一个方法可以合并location其他对象成员的输出。(成员与家庭有关,但这在这里并不重要。)
For example, given
例如,给定
- member_1 has
location== 'San Diego (traveling, returns 15 May)' - member_2 has
location== 'San Diego'
- member_1 具有
location== '圣地亚哥(旅行,5 月 15 日返回)' - member_2 有
location== '圣地亚哥'
Family.location might return 'San Diego (member_1 traveling, returns 15 May)' The specifics are unimportant.
Family.location 可能会返回“圣地亚哥(member_1 旅行,5 月 15 日返回)”具体细节并不重要。
To simplify the testing of Family.location, I want to stub Member.location. However, I need it to return two different (specified) values as in the example above. Ideally, these would be based on an attribute of member, but simply returning different values in a sequence would be OK. Is there a way to do this in RSpec?
为了简化 Family.location 的测试,我想存根 Member.location。但是,我需要它返回两个不同的(指定的)值,如上例所示。理想情况下,这些将基于 的属性member,但简单地按序列返回不同的值就可以了。有没有办法在 RSpec 中做到这一点?
It's possible to override the Member.location method within each test example, such as
可以在每个测试示例中覆盖 Member.location 方法,例如
it "when residence is the same" do
class Member
def location
return {:residence=>'Home', :work=>'his_work'} if self.male?
return {:residence=>'Home', :work=>'her_work'}
end
end
@family.location[:residence].should == 'Home'
end
but I doubt this is good practice. In any case, when RSpec is running a series of examples it doesn't restore the original class, so this kind of override "poisons" subsequent examples.
但我怀疑这是一种很好的做法。在任何情况下,当 RSpec 运行一系列示例时,它不会恢复原始类,因此这种覆盖“毒药”后续示例。
So, is there a way to have a stubbed method return different, specified values on each call?
那么,有没有办法让存根方法在每次调用时返回不同的指定值?
回答by idlefingers
You can stub a method to return different values each time it's called;
你可以存根一个方法在每次调用时返回不同的值;
allow(@family).to receive(:location).and_return('first', 'second', 'other')
So the first time you call @family.locationit will return 'first', the second time it will return 'second', and all subsequent times you call it, it will return 'other'.
因此,您第一次调用@family.location它时将返回“first”,第二次将返回“second”,随后所有调用它时,它将返回“other”。
回答by nothing-special-here
RSpec 3 syntax:
RSpec 3 语法:
allow(@family).to receive(:location).and_return("abcdefg", "bcdefgh")
回答by thisismydesign
The accepted solution should only be used if you have a specific number of calls and need a specific sequence of data. But what if you don't know the number of calls that will be made, or don't care about the order of dataonly that it's something different each time? As OP said:
仅当您有特定数量的调用并需要特定的数据序列时,才应使用已接受的解决方案。但是,如果您不知道将进行的调用次数,或者不关心数据的顺序只是每次都不同,该怎么办?正如 OP 所说:
simply returning different values in a sequence would be OK
简单地在序列中返回不同的值就可以了
The issue with and_returnis that the return value is memoized. Meaning even if you'd return something dynamic you'll always get the same.
问题and_return在于返回值被记忆。这意味着即使你返回一些动态的东西,你也会得到相同的结果。
E.g.
例如
allow(mock).to receive(:method).and_return(SecureRandom.hex)
mock.method # => 7c01419e102238c6c1bd6cc5a1e25e1b
mock.method # => 7c01419e102238c6c1bd6cc5a1e25e1b
Or a practical example would be using factories and getting the same IDs:
或者一个实际的例子是使用工厂并获得相同的 ID:
allow(Person).to receive(:create).and_return(build_stubbed(:person))
Person.create # => Person(id: 1)
Person.create # => Person(id: 1)
In these cases you can stub the method body to have the code executed every time:
在这些情况下,您可以存根方法体以每次都执行代码:
allow(Member).to receive(:location) do
{ residence: Faker::Address.city }
end
Member.location # => { residence: 'New York' }
Member.location # => { residence: 'Budapest' }
Note that you have no access to the Member object via selfin this context but can use variables from the testing context.
请注意,您无法self在此上下文中访问 Member 对象,但可以使用测试上下文中的变量。
E.g.
例如
member = build(:member)
allow(member).to receive(:location) do
{ residence: Faker::Address.city, work: member.male? 'his_work' : 'her_work' }
end
回答by ndnenkov
If for some reason you want to use the old syntax, you can still:
如果出于某种原因您想使用旧语法,您仍然可以:
@family.stub(:location).and_return('foo', 'bar')
回答by matteo
I've tried the solution outline here above but it does not work for my. I solved the problem by stubbing with a substitute implementation.
我已经尝试了上面的解决方案大纲,但它对我不起作用。我通过使用替代实现进行存根解决了这个问题。
Something like:
就像是:
@family.stub(:location) { rand.to_s }

