javascript - jasmine, kalıtılmış yöntemlerde (typescript ile) toHaveBeenCalled() ile beklendiği gibi çalışmıyor
Şu anda TypeScript sınıflarında kalıtılmış yöntemleri çağrılar için gözlemlemeye çalışırken bir sorun yaşıyorum; toHaveBeenCalled() yöntemi, gözlemlenen yöntem çağrılmış olmasına rağmen false döndürüyor. Aşağıdaki senaryoya bakın...
TypeScript ile yazılmış iki sınıfım var
class Parent() {
buyFood() {
// buy food
}
}
class Husband extends Parent {
makeDinner() {
super.buyFood();
// make dinner;
}
}
Husband sınıfı için yaptığım testlerde, sadece akşam yemeği yapma mantığını test etmekle ilgileniyorum, çünkü süper sınıfın yemek alma mantığı kendi test dizisinde test ediliyor.
Bu nedenle, testlerim aşağıdakilere benzer bir şey gibi görünüyor.
let husband:Husband = new Husband();
it('Should make a good dinner', () => {
spyOn(husband, 'buyFood');
husband.makeDinner();
expect(husband.buyFood).toHaveBeenCalled();
}
buyFood() çağrılıyor olmasına rağmen, iddia, Parent sınıfından miras alınan yöntem olan husband.buyFood() hiçbir zaman çağrılmamış diyen bir hata ile başarısız oluyor.
buyFood() metod çağrısıyla değer değişikliklerini doğrulamak zorunda kalmadan bu konuyu nasıl ele almalıyım?
Cevap #1
TypeScript ve casusluğun arkasındaki mekanikleri anlamalısın.
Öncelikle Typescript üzerinde ...
class Parent() içindeki ekstra parantezleri göz ardı ediyorum.
Typescript perde arkasında prototipal mirası kullanır. Böylece bir prototip, referans verilen özellikleri "temel sınıf"tan yeni sınıfa kopyalar. İşte __extends() fonksiyonundaki for döngüsü bunu yapar.
Bu, Typescript'inizin çevrildiği ES5 kodudur:
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));
Bu Typescript oyun alanını kullanarak Typescript'i çevirebilirsiniz.
Senin super ifaden, üst sınıfın buyFood() metodunu çağırır ve “miras alınmış” Husband metodunu çağırmaz.
Çizgiyi gör
_super.prototype.buyFood.call(this);
ve _super referansını takip edin.
Şimdi Jasmine Casuslar ...
Bir casus, geçirilen nesnenin belirtilen işlevini bir vekil görevi görecek bir casus işleviyle değiştirecektir. Bu vekil artık çağrıları takip edebilir ve programlanmış davranışa bağlı olarak, orijinal işlevi çağırıp çağırmayacağını, bir sahteyi çağırıp çağırmayacağını, bir değer döndürüp döndürmeyeceğini veya hiçbir şey yapmayacağını (varsayılan) kontrol edebilir.
Çok basitleştirilmiş bir spyOn() şöyle görünebilir:
function spyOn(obj, fn) {
var origFn = obj[fn],
spy = function() {
spy.calls.push(arguments);
};
spy.calls = [];
obj[fn] = spy;
}
Gerçek casus yöntemi ise çok daha karmaşık.
Senin sıran
spyOn(husband, 'buyFood');
aslında Husband örneğindeki yöntemi bir casusla değiştirecek. Ancak, kod temel sınıfın (ebeveyn prototipin) referansını çağırdığı için, az önce değiştirdiğinizle aynı işlev değildir.
Çözüm
this ile referans verilen metodu ya aramalısınız
class Husband extends Parent {
makeDinner() {
// call byFood() via this
this.buyFood();
}
}
... veya üst prototipi (super) gözetlemek için:
it('Should make a good dinner', () => {
spyOn(Parent.prototype, 'buyFood');
husband.makeDinner();
expect(Parent.prototype.buyFood).toHaveBeenCalled();
}
Cevap #2
ES6 kullanılırken, Parent.prototype çalışmaz. Bunun yerine Object.getPrototypeOf kullanın.
Benim için işe yarayan şuydu:
it('Should make a good dinner', () => {
spyOn(Object.getPrototypeOf(Object.getPrototypeOf(husband)), 'buyFood');
husband.makeDinner();
expect(Parent.prototype.buyFood).toHaveBeenCalled();
}
