他のオブジェクト、Membersのlocation
出力をマージするメソッドlocation
を持つモデルファミリがあります。 (メンバーは家族に関連付けられていますが、ここでは重要ではありません。)
たとえば、与えられた
location
== 'サンディエゴ(旅行、5月15日を返します)'がありますlocation
== 'San Diego'がありますFamily.locationが「San Diego(member_1 travelling、return 15 May)」を返す場合があります。詳細は重要ではありません。
Family.locationのテストを簡単にするために、Member.locationをスタブ化します。ただし、上記の例のように2つの異なる(指定された)値を返す必要があります。理想的には、これらは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が一連の例を実行しているとき、元のクラスは復元されないため、この種の後続の例は "poisons"をオーバーライドします。
それで、スタブ化されたメソッドが各呼び出しで異なる指定された値を返すようにする方法はありますか?
メソッドをスタブして、呼び出されるたびに異なる値を返すことができます。
allow(@family).to receive(:location).and_return('first', 'second', 'other')
したがって、最初に@family.location
を呼び出すと「first」が返され、2回目には「second」が返され、それ以降に呼び出すと「other」が返されます。
RSpec 3構文:
allow(@family).to receive(:location).and_return("abcdefg", "bcdefgh")
承認されたソリューションは、特定の数の呼び出しがあり、特定のデータシーケンスが必要な場合にのみ使用してください。しかし、行われる呼び出しの数がわからない場合、またはデータの順序を気にしない場合毎回何か違うというだけですか? OPが言ったように:
単にシーケンス内の異なる値を返すだけでもOKです
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 }