jasmine.createSpyObj avec des propriétés
Lorsque je me moque des dépendances dans mes tests Angular, je crée généralement un objet espion en utilisant jasmine.createSpyObj:
const serviceSpy= jasmine.createSpyObj('MyService', ['method']);
puis fournissez-le au TestBed:
providers: [
{provide: MyService, useValue: serviceSpy}
]
Lorsque je l'utilise dans mon test, je peux alors spécifier la valeur de retour souhaitée:
serviceSpy.method.and.returnValue(of([...]));
Maintenant, j'ai également besoin de me moquer des propriétés et je ne peux pas savoir comment cela doit être fait. createSpyObjautorise la définition des noms de propriétés:
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);
mais j'ai essayé différentes solutions basées sur les nombreux articles et réponses sans succès, par exemple:
// 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;
La seule façon dont je pourrais le faire fonctionner à moitié est:
let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});
Le problème ici est qu'il s'agit d'un ensemble unique à la création. Si je veux changer la valeur attendue dans le test, cela ne fonctionne pas.
fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';
Existe-t-il une solution à la fois aux méthodes et aux propriétés fictives en créant un objet espion, ou dois-je créer ma propre fausse classe sur laquelle je peux ensuite utiliser spyOnet spyOnProperty?
Je voudrais également savoir quelle est l'utilisation du tableau de propriétés dans la createSpyObjdéfinition. Jusqu'à présent, je n'ai vu aucun exemple sur le Web qui l'explique.
Réponses
D'après la documentation (c'est moi qui souligne):
Vous pouvez créer rapidement un objet espion avec plusieurs propriétés en transmettant un tableau ou un hachage de propriétés comme troisième argument à
createSpyObj. Dans ce cas, vous n'aurez pas de référence aux espions créés, donc si vous devez modifier leurs stratégies d'espionnage plus tard, vous devrez utiliser l'Object.getOwnPropertyDescriptorapproche .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); });
Les propriétés espionnées sont des descripteurs (voir par exemple Object.definePropertysur MDN ), donc pour accéder aux objets espions, vous devez obtenir l'objet descripteur puis interagir avec les méthodes getet qui y sont setdéfinies.
Dans TypeScript, le compilateur a besoin d'un peu d'aide. createSpyObjrenvoie soit anyou SpyObj<T>, et a SpyObjdéfinit uniquement les méthodes comme étant espionnées:
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
};
Donc, pour accéder .andau getter du descripteur, vous aurez besoin d' un chaînage optionnel (comme Object.getOwnPropertyDescriptorpeut le renvoyer undefined) et d'une assertion de type à a Spy:
(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);
Terrain de jeux
Merci beaucoup @jonrsharpe! Je viens d'ajouter une fonction pour pouvoir faire:
spyPropertyGetter(spy, 'propName').and.returnValue(...)
Qui est défini comme:
function spyPropertyGetter(
spyObj: jasmine.SpyObj<any>,
propName: string
) {
return Object.getOwnPropertyDescriptor(spyObj, propName).get as jasmine.Spy<
jasmine.Func
>;
}