jasmine.createSpyObj mit Eigenschaften
Wenn ich Abhängigkeiten in meinen Angular-Tests verspotte, erstelle ich normalerweise ein Spionageobjekt mit jasmine.createSpyObj:
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
Stellen Sie es dann dem TestBed zur Verfügung:
providers: [
{provide: MyService, useValue: serviceSpy}
]
Wenn ich es in meinem Test verwende, kann ich den gewünschten Rückgabewert angeben:
serviceSpy.method.and.returnValue(of([...]));
Jetzt muss ich auch Eigenschaften verspotten und kann nicht herausfinden, wie es gemacht werden soll. createSpyObjerlaubt die Definition von Eigenschaftsnamen:
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);
Aber ich habe verschiedene Lösungen ausprobiert, basierend auf den zahlreichen Artikeln und Antworten, ohne Erfolg, z.
// Cannot read property 'and' of undefined
serviceSpy.property.and.returnValue(true);
// not declared configurable
spyOnProperty(serviceSpy, 'property').and.returnValue(true);
// no build errors, but value stays 'undefined'
serviceSpy.property = true;
Der einzige Weg, wie ich es zur "halben" Arbeit machen kann, ist:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
Das Problem hierbei ist, dass es sich bei der Erstellung um einen einmaligen Satz handelt. Wenn ich den erwarteten Wert im Test ändern möchte, funktioniert dies nicht.
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
Gibt es eine Lösung für Scheinmethoden und -eigenschaften durch Erstellen eines Spionageobjekts, oder sollte ich meine eigene gefälschte Klasse erstellen, für die ich dann spyOnund verwenden kann spyOnProperty?
Ich würde auch gerne wissen, wie das Eigenschaftenarray in der createSpyObjDefinition verwendet wird. Bisher habe ich im Web kein Beispiel gesehen, das dies erklärt.
Antworten
Gemäß der Dokumentation (Schwerpunkt Mine):
Sie können schnell ein Spionageobjekt mit mehreren Eigenschaften erstellen, indem Sie ein Array oder einen Hash von Eigenschaften als drittes Argument an übergeben
createSpyObj. In diesem Fall haben Sie keinen Verweis auf die erstellten Spione. Wenn Sie also deren Spionagestrategien später ändern müssen, müssen Sie denObject.getOwnPropertyDescriptorAnsatz verwenden .it("creates a spy object with properties", function() { let obj = createSpyObj("myObject", {}, { x: 3, y: 4 }); expect(obj.x).toEqual(3); Object.getOwnPropertyDescriptor(obj, "x").get.and.returnValue(7); expect(obj.x).toEqual(7); });
Spied Eigenschaften sind Deskriptoren (siehe zB Object.definePropertyauf MDN ), so den Zugriff auf die Spionage - Objekte , die Sie den Descriptor - Objekt erhalten müssen dann interact mit den getund setMethoden definiert.
In TypeScript benötigt der Compiler etwas Hilfe. createSpyObjGibt entweder anyoder zurück SpyObj<T>, und a SpyObjdefiniert die Methoden nur als ausspioniert:
type SpyObj<T> = T & {
[K in keyof T]: T[K] extends Func ? T[K] & Spy<T[K]> : T[K];
// | if it's a | spy on it | otherwise leave
// | callable | | it alone
};
Um .andauf den Getter des Deskriptors zugreifen zu können, benötigen Sie eine optionale Verkettung (wie Object.getOwnPropertyDescriptorzurückgegeben undefined) und eine Typzusicherung an a Spy:
(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);
Spielplatz
Vielen Dank @jonrsharpe! Ich habe gerade eine Funktion hinzugefügt, damit ich Folgendes tun kann:
spyPropertyGetter(spy, 'propName').and.returnValue(...)
Welches ist definiert als:
function spyPropertyGetter(
spyObj: jasmine.SpyObj<any>,
propName: string
) {
return Object.getOwnPropertyDescriptor(spyObj, propName).get as jasmine.Spy<
jasmine.Func
>;
}