我有一个带有方法 location
的模型 Family,它合并了其他对象 Members 的 location
输出。 (成员与家庭有关,但这在这里并不重要。)
例如,给定
member_1 有 location == '圣地亚哥(旅行,5 月 15 日返回)'
member_2 的位置 == '圣地亚哥'
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 运行一系列示例时,它不会恢复原始类,因此这种覆盖“毒害”了后续示例。
那么,有没有办法让存根方法在每次调用时返回不同的指定值?
您可以存根方法以在每次调用时返回不同的值;
allow(@family).to receive(:location).and_return('first', 'second', 'other')
所以第一次调用 @family.location
时,它会返回 'first',第二次会返回 'second',而所有后续调用它的时候,它都会返回 'other'。
RSpec 3 语法:
allow(@family).to receive(:location).and_return("abcdefg", "bcdefgh")
仅当您有特定数量的调用并需要特定的数据序列时,才应使用公认的解决方案。但是,如果您不知道将要进行的调用次数,或者不关心数据的顺序,只是因为每次都不同,该怎么办?正如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
如果由于某种原因您想使用旧语法,您仍然可以:
@family.stub(:location).and_return('foo', 'bar')
我已经尝试了上面的解决方案大纲,但它不适用于我。我通过使用替代实现来解决问题。
就像是:
@family.stub(:location) { rand.to_s }
不定期副业成功案例分享
my_big_array
的长字符串数组,您可以使用allow(@family).to receive(:location).and_return(*my_big_array)
。希望这可以帮助。