ruby-on-rails RSpec存根方法可以按顺序返回不同的值吗?

wi3ka0sx  于 2023-01-03  发布在  Ruby
关注(0)|答案(6)|浏览(176)

我有一个带有location方法的模型Family,该方法合并了其他对象Members的location输出(Members与Family相关联,但这在这里并不重要)。
例如,给定

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

位置可能返回"San Diego(member_1 traveling,returns 15 May)"具体信息并不重要。
为了简化Family. location的测试,我想stub 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运行一系列示例时,它不会恢复原来的类,所以这种覆盖会"毒害"后续的示例。
那么,有没有办法让stubed方法在每次调用时返回不同的指定值呢?

v9tzhpje

v9tzhpje1#

您可以存根一个方法,以便在每次调用它时返回不同的值;

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

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

34gzjxbg

34gzjxbg2#

RSpec 3语法:

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

ars1skjm3#

公认的解决方案应该只在你有一个特定的调用次数和需要一个特定的数据序列时使用。但是如果你不知道将要进行的调用次数,或者不关心数据的顺序只是每次都不同呢?正如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
toe95027

toe950274#

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

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

vohkndzv5#

我已经尝试了上面的解决方案,但是它对我的不起作用。我用一个替代实现来解决这个问题。
例如:

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

bqjvbblv6#

我遇到了一个问题,我多次调用一个方法,而且顺序不总是相同的,在这种情况下,我建议使用.with,使用方法的参数来区分示例。
例如,这可能是您的“默认”返回值:

allow(@family).to receive(:location).and_return('her_work')

但是,如果location接收到像“male”这样的参数,则可以添加:

allow(@family).to receive(:location).with("male").and_return('his_work')

有许多不同的匹配参数类型可以用于.with
https://relishapp.com/rspec/rspec-mocks/v/3-2/docs/setting-constraints/matching-arguments

相关问题