Ich habe eine Modellfamilie mit einer Methode location
, die die location
-Ausgaben anderer Objekte, Members, zusammenführt. (Mitglieder sind mit Familien verbunden, aber das ist hier nicht wichtig.)
Zum Beispiel gegeben
location
== 'San Diego (reisen, kehrt am 15. Mai zurück)'location
== 'San Diego'Family.location gibt möglicherweise 'San Diego (Mitglied_1 Reisen, Rückkehr 15. Mai)' zurück. Die Details sind unwichtig.
Um das Testen von Family.location zu vereinfachen, möchte ich Member.location stubben. Ich brauche jedoch zwei verschiedene (angegebene) Werte wie im obigen Beispiel. Im Idealfall würden diese auf einem Attribut von member
basieren, aber die Rückgabe verschiedener Werte in einer Sequenz wäre OK. Gibt es eine Möglichkeit, dies in RSpec zu tun?
Es ist möglich, die Member.location-Methode in jedem Testbeispiel zu überschreiben, z. B.
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
aber ich bezweifle, dass dies eine gute Praxis ist. In jedem Fall stellt RSpec, wenn eine Reihe von Beispielen ausgeführt wird, die ursprüngliche Klasse nicht wieder her, so dass diese Art von "Gifte" nachfolgende Beispiele überschreibt.
Gibt es eine Möglichkeit, eine Stubbed-Methode bei jedem Aufruf andere, angegebene Werte zurückgeben zu lassen?
Sie können eine Methode stubben, um bei jedem Aufruf andere Werte zurückzugeben.
allow(@family).to receive(:location).and_return('first', 'second', 'other')
Wenn Sie @family.location
zum ersten Mal aufrufen, wird 'first' zurückgegeben, beim zweiten 'second' und bei allen folgenden Anrufen wird 'other' angezeigt.
RSpec 3-Syntax:
allow(@family).to receive(:location).and_return("abcdefg", "bcdefgh")
Wenn Sie aus irgendeinem Grund die alte Syntax verwenden möchten, können Sie noch Folgendes tun:
@family.stub(:location).and_return('foo', 'bar')
Die akzeptierte Lösung sollte nur verwendet werden, wenn Sie eine bestimmte Anzahl von Anrufen haben und eine bestimmte Datensequenz benötigen. Aber was ist, wenn Sie nicht wissen, wie viele Anrufe getätigt werden, oder sich die Reihenfolge der Daten nicht interessiert nur dass es jedes Mal etwas anderes ist? Wie OP sagte:
einfach andere Werte in einer Sequenz zurückgeben, wäre in Ordnung
Das Problem mit and_return
ist, dass der Rückgabewert gespeichert wird. Das heißt, auch wenn Sie etwas Dynamisches zurückgeben, erhalten Sie immer dasselbe.
Z.B.
allow(mock).to receive(:method).and_return(SecureRandom.hex)
mock.method # => 7c01419e102238c6c1bd6cc5a1e25e1b
mock.method # => 7c01419e102238c6c1bd6cc5a1e25e1b
Oder ein praktisches Beispiel wäre die Verwendung von Fabriken und das Erhalten der gleichen IDs:
allow(Person).to receive(:create).and_return(build_stubbed(:person))
Person.create # => Person(id: 1)
Person.create # => Person(id: 1)
In diesen Fällen können Sie den Methodenkörper stubben, damit der Code jedes Mal ausgeführt wird:
allow(Member).to receive(:location) do
{ residence: Faker::Address.city }
end
Member.location # => { residence: 'New York' }
Member.location # => { residence: 'Budapest' }
Beachten Sie, dass Sie in diesem Kontext nicht über self
auf das Member-Objekt zugreifen können, sondern Variablen aus dem Testkontext verwenden können.
Z.B.
member = build(:member)
allow(member).to receive(:location) do
{ residence: Faker::Address.city, work: member.male? 'his_work' : 'her_work' }
end
Ich habe die Lösungsübersicht hier oben ausprobiert, funktioniert aber nicht für meine. Ich löste das Problem durch Stubbing mit einer Ersatzimplementierung.
So etwas wie:
@family.stub(:location) { Rand.to_s }