it-swarm.com.de

Kann die Stubbed-Methode von RSpec nacheinander unterschiedliche Werte zurückgeben?

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

  • member_1 hat location == 'San Diego (reisen, kehrt am 15. Mai zurück)'
  • member_2 hat 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?

58
Mike Blyth

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.

126
idlefingers

RSpec 3-Syntax:

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

Wenn Sie aus irgendeinem Grund die alte Syntax verwenden möchten, können Sie noch Folgendes tun:

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

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
0
thisismydesign

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 }
0
matteo