jasmine.createSpyObj con proprietà
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
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
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
>;
}