jasmine.createSpyObj con proprietà

Oct 28 2020

Quando prendo in giro le dipendenze nei miei test angolari, di solito creo un oggetto spia usando jasmine.createSpyObj:

const serviceSpy= jasmine.createSpyObj('MyService', ['method']);

quindi fornirlo al TestBed:

  providers: [
    {provide: MyService, useValue: serviceSpy}
  ]

Quando lo uso nel mio test, posso quindi specificare il valore di ritorno desiderato:

serviceSpy.method.and.returnValue(of([...]));

Ora devo anche simulare le proprietà e non riesco a scoprire come dovrebbe essere fatto. createSpyObjconsente la definizione dei nomi delle proprietà:

const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);

ma ho provato varie soluzioni in base ai numerosi articoli e risposte disponibili senza alcun successo, ad esempio:

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

L'unico modo in cui potrei farlo funzionare "a metà" è:

let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});

Il problema qui è che è un set unico alla creazione. Se voglio modificare il valore atteso nel test, non funziona.

fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';

Esiste una soluzione sia per i metodi fittizi che per le proprietà creando un oggetto spia, o devo creare la mia classe falsa su cui posso quindi utilizzare spyOne spyOnProperty?

Vorrei anche sapere qual è l'uso dell'array delle proprietà nella createSpyObjdefinizione. Finora non ho visto alcun esempio sul web che lo spieghi.

Risposte

5 jonrsharpe Oct 28 2020 at 01:29

Secondo la documentazione (enfasi mia):

È possibile creare rapidamente un oggetto spia con diverse proprietà passando un array o un hash di proprietà come terzo argomento a createSpyObj. In questo caso non avrai un riferimento alle spie create, quindi se devi cambiare le loro strategie di spionaggio in un secondo momento, dovrai utilizzare l' Object.getOwnPropertyDescriptorapproccio .

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

Le proprietà spiate sono descrittori (vedi ad esempio Object.definePropertysu MDN ), quindi per accedere agli oggetti spia è necessario ottenere l'oggetto descrittore e quindi interagire con i metodi gete setdefiniti su di esso.


In TypeScript, il compilatore ha bisogno di un po 'di aiuto. createSpyObjrestituisce o anyo SpyObj<T>, e a SpyObjdefinisce solo i metodi come spiati:

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

Quindi, per accedere .andal getter del descrittore, avrai bisogno del concatenamento opzionale (come Object.getOwnPropertyDescriptorpotrebbe restituire undefined) e di un'asserzione di tipo a Spy:

(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);

Terreno di gioco

1 IanGrainger Nov 11 2020 at 17:57

Grazie mille @jonrsharpe! Ho appena aggiunto una funzione in modo da poter fare:

spyPropertyGetter(spy, 'propName').and.returnValue(...)

Che è definito come:

function spyPropertyGetter(
  spyObj: jasmine.SpyObj<any>,
  propName: string
) {
  return Object.getOwnPropertyDescriptor(spyObj, propName).get as jasmine.Spy<
    jasmine.Func
  >;
}