Je rencontre actuellement un problème lors de l'espionnage des méthodes héritées pour les appels dans les classes TypeScript, où la méthode toHaveBeenCalled () renvoie false, même si la méthode espionnée est appelée. Regardez le scénario suivant ...
J'ai deux classes, écrites en TypeScript
class Parent() {
buyFood() {
// buy food
}
}
class Husband extends Parent {
makeDinner() {
super.buyFood();
// make dinner;
}
}
Dans mes tests pour la classe Husband, je ne fais que tester la logique de préparation du dîner, car la logique de l'achat de nourriture de la super classe est testée dans sa propre suite de tests.
Par conséquent, mes tests ressemblent à quelque chose du genre suivant.
let husband:Husband = new Husband();
it('Should make a good dinner', () => {
spyOn(husband, 'buyFood');
husband.makeDinner();
expect(husband.buyFood).toHaveBeenCalled();
}
Même si buyFood () est appelé, l'assertion échoue avec une erreur indiquant que mari.buyFood () qui est la méthode héritée de la classe Parent n'a jamais été appelée.
Comment dois-je résoudre ce problème sans avoir à affirmer les changements de valeur par l'appel de la méthode buyFood ()?
Vous devez comprendre la mécanique derrière TypeScript et l'espionnage.
J'ignore les parens supplémentaires dans class Parent()
.
TypeScript utilise l'héritage prototypique derrière le rideau. Ainsi, un prototype copiera les propriétés référencées de la "classe de base" vers la nouvelle classe. C'est ce que fait la boucle for
dans la fonction __extends()
.
Il s'agit du code ES5 dans lequel votre TypeScript est traduit:
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var Parent = (function () {
function Parent() {
}
Parent.prototype.buyFood = function () {
// buy food
};
return Parent;
}());
var Husband = (function (_super) {
__extends(Husband, _super);
function Husband() {
return _super.apply(this, arguments) || this;
}
Husband.prototype.makeDinner = function () {
_super.prototype.buyFood.call(this);
// make dinner;
};
return Husband;
}(Parent));
Vous pouvez traduire TypeScript en utilisant cette aire de jeux TypeScript .
Votre expression super
appelle la méthode buyFood()
de la classe parente et non la méthode de la "hérité" Husband
.
Voir la ligne
_super.prototype.buyFood.call(this);
et suivez la référence _super
.
Un espion remplacera la fonction nommée de l'objet passé par une fonction d'espionnage qui agira comme un proxy. Ce proxy peut désormais suivre les appels et, selon le comportement programmé, contrôler s'il faut appeler la fonction d'origine, un faux, renvoyer une valeur ou ne rien faire (par défaut).
Un très simplifié spyOn()
pourrait ressembler à ceci:
function spyOn(obj, fn) {
var origFn = obj[fn],
spy = function() {
spy.calls.Push(arguments);
};
spy.calls = [];
obj[fn] = spy;
}
La méthode d'espionnage réelle est cependant beaucoup plus complexe.
Votre ligne
spyOn(husband, 'buyFood');
remplacera en fait la méthode dans l'instance de Husband
par un espion. Mais, puisque le code appelle la référence de la classe de base (le prototype parent), ce n'est pas la même fonction que vous venez de remplacer.
Vous devez soit appeler la méthode référencée this
class Husband extends Parent {
makeDinner() {
// call byFood() via this
this.buyFood();
}
}
... ou espionner le prototype parent (super
):
it('Should make a good dinner', () => {
spyOn(Parent.prototype, 'buyFood');
husband.makeDinner();
expect(Parent.prototype.buyFood).toHaveBeenCalled();
}
Lorsque vous utilisez ES6, le Parent.prototype
ne fonctionnera pas. Utilisation Object.getPrototypeOf
au lieu.
C'est ce qui a fonctionné pour moi:
it('Should make a good dinner', () => {
spyOn(Object.getPrototypeOf(Object.getPrototypeOf(husband), 'buyFood');
husband.makeDinner();
expect(Parent.prototype.buyFood).toHaveBeenCalled();
}