Gibt es eine Möglichkeit, Jasmine-Spione anhand von Argumenten zu ändern?

146

Ich möchte eine Funktion testen, die eine externe API-Methode zweimal mit verschiedenen Parametern aufruft. Ich möchte diese externe API mit einem Jasmine-Spion verspotten und basierend auf den Parametern verschiedene Dinge zurückgeben. Gibt es eine Möglichkeit, dies in Jasmine zu tun? Das Beste, was ich mir einfallen lassen kann, ist ein Hack mit andCallFake:

var functionToTest = function() {
  var userName = externalApi.get('abc');
  var userId = externalApi.get('123');
};


describe('my fn', function() {
  it('gets user name and ID', function() {
    spyOn(externalApi, 'get').andCallFake(function(myParam) {
      if (myParam == 'abc') {
        return 'Jane';
      } else if (myParam == '123') {
        return 98765;
      }
    });
  });
});
Jmr
quelle

Antworten:

212

In Jasmine-Versionen 3.0 und höher können Sie verwenden withArgs

describe('my fn', function() {
  it('gets user name and ID', function() {
    spyOn(externalApi, 'get')
      .withArgs('abc').and.returnValue('Jane')
      .withArgs('123').and.returnValue(98765);
  });
});

Für Jasmine-Versionen vor 3.0 callFakeist dies der richtige Weg, aber Sie können es vereinfachen, indem Sie ein Objekt verwenden, um die Rückgabewerte zu speichern

describe('my fn', function() {
  var params = {
    'abc': 'Jane', 
    '123': 98765
  }

  it('gets user name and ID', function() {
    spyOn(externalApi, 'get').and.callFake(function(myParam) {
     return params[myParam]
    });
  });
});

Abhängig von der Version von Jasmine unterscheidet sich die Syntax geringfügig:

  • 1.3.1: .andCallFake(fn)
  • 2.0: .and.callFake(fn)

Ressourcen:

Andreas Köberle
quelle
11
Dies ist jetzt and.callFake- jasmine.github.io/2.2/… >
Lucy Bain
Ich musste verschiedene Versprechen zurückgeben, daher sah die Rückgabe etwas anders aus: return q.when (params [myParam]);. Ansonsten war dies eine perfekte Lösung für mein Problem. Meine Traumlösung wäre, "and.returnValue" -Aufrufe zu ändern.
Bill Turner
7
fühlt sich an, als ob Jasmin eine bessere Möglichkeit haben sollte, dies zu erklären. Wie spyOn(fake, 'method').withArgs('abc').and.returnValue('Jane')und spyOn(fake, 'method').withArgs('123').and.returnValue(98765).
Jrharshath
@ Jrharshath .withArgsarbeitet nicht für mich in Jasmin 2.0
Hemkaran_raghav
1
.withArgsist nicht wirklich verfügbar - ich meinte, dass eine solche Methode beim Schreiben von Tests Sinn machen würde.
Jrharshath
9

Sie können auch $provideeinen Spion erstellen. Und verspotten mit and.returnValuesanstatt and.returnValueparametrisierte Daten zu übergeben.

Gemäß Jasmine-Dokumenten: Wenn Sie den Spion mit verketten and.returnValues, geben alle Aufrufe der Funktion bestimmte Werte in der Reihenfolge zurück, bis das Ende der Liste der Rückgabewerte erreicht ist. An diesem Punkt wird für alle nachfolgenden Aufrufe undefiniert zurückgegeben.

describe('my fn', () => {
    beforeEach(module($provide => {
        $provide.value('externalApi', jasmine.createSpyObj('externalApi', ['get']));
    }));

        it('get userName and Id', inject((externalApi) => {
            // Given
            externalApi.get.and.returnValues('abc','123');

            // When
            //insert your condition

            // Then
            // insert the expectation                
        }));
});
Akhouri
quelle
Dies ist die richtige Antwort, da ein Test immer genau wissen sollte, wie ein Spion angerufen wird, und daher nur returnValueszur Unterstützung mehrerer Anrufe verwendet werden sollte
Schmuli
2
Nur um Akhouris Antwort zu verdeutlichen: Diese Methode funktioniert nur, wenn das externalApi.get.and.returnValues('abc','123')innerhalb der itFunktion aufgerufen wird . Andernfalls funktioniert es niemals, wenn Sie eine Liste mit Werten festlegen, andernfalls wo, da die Reihenfolge, in der die Tests ausgeführt werden, nicht vorhersehbar ist. Tatsächlich sollten Tests nicht von der Reihenfolge abhängen, in der sie ausgeführt werden.
avi.elkharrat
0

In meinem Fall hatte ich eine Komponente, die ich getestet habe, und in ihrem Konstruktor gibt es einen Konfigurationsdienst mit einer Methode namens getAppConfigValue , die zweimal aufgerufen wird, jedes Mal mit unterschiedlichen Argumenten:

constructor(private configSvc: ConfigService) {
  this.configSvc.getAppConfigValue('a_string');
  this.configSvc.getAppConfigValue('another_string');
}

In meiner Spezifikation habe ich den ConfigService im TestBed wie folgt bereitgestellt:

{
  provide: ConfigService,
  useValue: {
    getAppConfigValue: (key: any): any {
      if (key === 'a_string) {
        return 'a_value';
      } else if (key === 'another_string') {
        return 'another_value';
      }
    }
  } as ConfigService
}

Solange die Signatur für getAppConfigValue mit der im tatsächlichen ConfigService angegebenen übereinstimmt, kann die interne Funktion der Funktion geändert werden.

Guillermo
quelle