RSpec stubbed方法可以按顺序返回不同的值吗?

时间:2011-05-10 09:29:17

标签: ruby-on-rails testing rspec stub

我有一个模型Family,其方法location合并了其他对象location的输出。 (成员与家庭有关,但这在这里并不重要。)

例如,给定

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

Family.location可能会返回'圣地亚哥(会员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运行一系列示例时,它不会恢复原始类,因此这种覆盖“毒药”后续示例。

那么,有没有办法让stubbed方法在每次调用时返回不同的指定值?

5 个答案:

答案 0 :(得分:130)

您可以将方法存根以在每次调用时返回不同的值;

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

因此,当您第一次拨打@family.location时,它将返回'first',第二次返回'second',以及随后所有调用它,它将返回'other'。

答案 1 :(得分:12)

RSpec 3语法:

allow(@family).to receive(:location).and_return("abcdefg", "bcdefgh")

答案 2 :(得分:1)

如果由于某种原因您想使用旧语法,您仍然可以:

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

答案 3 :(得分:1)

仅当您有特定数量的呼叫并且需要特定的数据序列时,才应使用接受的解决方案。但是如果您不知道将要拨打的电话数量,或者不关心数据的顺序怎么办,只是每次都有所不同?正如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' }

请注意,在这种情况下,您无法通过self访问Member对象,但可以使用测试上下文中的变量。

例如

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

答案 4 :(得分:0)

我已经尝试过上面的解决方案大纲,但它不适用于我的。我通过替换实现来解决问题。

类似的东西:

@family.stub(:location) { rand.to_s }