jasmine.createSpyObj mit Eigenschaften

Oct 28 2020

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

5 jonrsharpe Oct 28 2020 at 01:29

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 den Object.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

1 IanGrainger Nov 11 2020 at 17:57

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
  >;
}