RSpec存根方法是否可以按顺序返回不同的值?


74

我有一个模型家庭模型,该模型location可以合并location其他对象(成员)的输出。(成员与家庭有联系,但这在这里并不重要。)

例如,给定

  • member_1具有location=='圣地亚哥(旅行,返回5月15日)'
  • member_2具有location=='圣地亚哥'

Family.location可能返回“圣地亚哥(member_1出行,5月15日返回)”,具体情况并不重要。

为了简化Family.location的测试,我想对Member.location进行存根。但是,我需要它返回两个不同的(指定的)值,如上例所示。理想情况下,它们将基于的属性member,但是简单地按顺序返回不同的值就可以了。有没有办法在RSpec中做到这一点?

可以在每个测试示例中覆盖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

但是我怀疑这是个好习惯。无论如何,当RSpec运行一系列示例时,它不会还原原始类,因此,这种重写会“毒化”后续示例。

因此,有没有一种方法可以使存根方法在每次调用时返回不同的指定值?

Answers:


162

您可以在方法每次调用时都对它进行存根以便返回不同的值。

allow(@family).to receive(:location).and_return('first', 'second', 'other')

因此,第一次调用@family.location它将返回“ first”,第二次将返回“ second”,并且随后的所有调用都将返回“ other”。


谢谢@idlefingers!如果要返回大量值怎么办?
La-comadreja

4
@ La-comadreja说,您有很多叫的字符串my_big_array,可以这样做allow(@family).to receive(:location).and_return(*my_big_array)。希望这可以帮助。
idlefingers

3
第一次调用某个错误并第二次返回一个值怎么办?
詹姆斯·克莱因

@JamesKlein我正在尝试做同样的事情。你有没有解决?
theblang

我使用了提到的建议 这里,它实际上是使用您自己增加的块和计数器。
theblang


6

仅当您有特定数量的呼叫并且需要特定的数据序列时,才应使用接受的解决方案。但是,如果您不知道将要拨打的电话数量,或者不关心数据的顺序怎么办只在每次?正如OP所说:

只需按顺序返回不同的值就可以了

与问题 and_return在于返回值已被记忆。这意味着即使您返回动态的东西,您也将始终得到相同的东西。

例如

allow(mock).to receive(:method).and_return(SecureRandom.hex)
mock.method # => 7c01419e102238c6c1bd6cc5a1e25e1b
mock.method # => 7c01419e102238c6c1bd6cc5a1e25e1b

或者一个实际的例子是使用工厂并获得相同的ID:

allow(Person).to receive(:create).and_return(build_stubbed(:person))
Person.create # => Person(id: 1)
Person.create # => Person(id: 1)

在这些情况下,您可以对方法主体进行存根以便每次都执行代码:

allow(Member).to receive(:location) do
  { residence: Faker::Address.city }
end
Member.location # => { residence: 'New York' }
Member.location # => { residence: 'Budapest' }

请注意,您无权通过以下方式访问Member对象 self在此上下文,但可以使用测试上下文中的变量。

例如

member = build(:member)
allow(member).to receive(:location) do
  { residence: Faker::Address.city, work: member.male? 'his_work' : 'her_work' }
end

如果您需要多次调用“允许接收”,则传递一个块而不是使用“ and_return()”
Rodrigo Dias

1

如果出于某种原因要使用旧语法,则仍然可以:

@family.stub(:location).and_return('foo', 'bar')

0

我已经在上面尝试了解决方案概述,但不适用于我。我通过存入替代实现解决了这个问题。

就像是:

@family.stub(:location) { rand.to_s }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.