Jasmine ile Angular/TypeScript için özel metodlar için birim testi nasıl yazılır
Angular 2'de özel bir fonksiyon nasıl test edilir?
class FooBar {
private _status: number;
constructor( private foo : Bar ) {
this.initFooBar();
}
private initFooBar(){
this.foo.bar( "data" );
this._status = this.fooo.foo();
}
public get status(){
return this._status;
}
}
Bulduğum çözüm
Test kodunu kendisi kapanışın içine koyun veya dış kapsamda mevcut nesnelerdeki yerel değişkenlerin referanslarını saklayan kodu kapanışın içine ekleyin.
Daha sonra bir araç kullanarak test kodunu çıkarın. http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/
Eğer yaptıysanız bu problemi çözmek için bana daha iyi bir yol önerebilir misiniz?
Not:
Bu tür benzer soruların çoğu probleme çözüm getirmiyor, bu yüzden bu soruyu soruyorum
Çoğu geliştirici özel fonksiyonları test etmemen gerektiğini söyler ama ben onların doğru ya da yanlış olduğunu söylemiyorum, ama benim durumumda özel olanları test etme gereklilikleri var.
Cevap #1
Seninleyim, "sadece genel API'yi birim testi yapmak" iyi bir hedef olsa da, bazen bunun o kadar basit görünmediği ve API ile birim testleri arasında bir seçim yapmak zorunda gibi hissettiğin zamanlar olur. Bunu zaten biliyorsun, çünkü tam olarak yapmak istediğini soruyorsun, bu yüzden buna girmeyeceğim. :)
TypeScript'te, birim testi amacıyla özel üyelere erişmenin birkaç yolunu keşfettim. Bu sınıfı düşünün:
class MyThing {
private _name:string;
private _count:number;
constructor() {
this.init("Test", 123);
}
private init(name:string, count:number){
this._name = name;
this._count = count;
}
public get name(){ return this._name; }
public get count(){ return this._count; }
}
TS, sınıf üyelerine private, protected, public kullanarak erişimi kısıtlasa da, derlenmiş JS'de özel üyeler yoktur, çünkü bu JS'de bir şey değildir. Bu tamamen TS derleyicisi için kullanılır. Bu nedenle:
Erişim kısıtlamaları hakkında sizi uyarmaması için derleyiciyi atlatmak amacıyla
any'ye atama yapabilirsiniz:(thing as any)._name = "Unit Test"; (thing as any)._count = 123; (thing as any).init("Unit Test", 123);Bu yaklaşımın sorunu, derleyicinin
anyhakkındaki işleminizi tam olarak bilmemesi, bu yüzden istediğiniz tür hatalarını alamamanızdır:(thing as any)._name = 123; // wrong, but no error (thing as any)._count = "Unit Test"; // wrong, but no error (thing as any).init(0, "123"); // wrong, but no errorBu açıkça yeniden yapılandırmayı daha zor hale getirecek.
Özel üyelere erişmek için dizi erişimini (
[]) kullanabilirsiniz:thing["_name"] = "Unit Test"; thing["_count"] = 123; thing["init"]("Unit Test", 123);Görünüşü garip olsa da, TSC aslında tıpkı doğrudan erişiyormuşsunuz gibi tipleri doğrulayacaktır:
thing["_name"] = 123; // type error thing["_count"] = "Unit Test"; // type error thing["init"](0, "123"); // argument errorDürüst olmak gerekirse, bunun neden işe yaradığını bilmiyorum.Görünüşe göre bu, tür güvenliğini kaybetmeden özel üyelere erişmenizi sağlayan kasıtlı bir "kaçış yolu"dur. Bu, birim testleriniz için tam olarak istediğiniz şey bence.
İşte TypeScript Playground'da çalışan bir örnek.
TypeScript 2.6 için Düzenle
Bazılarının hoşuna giden bir diğer seçenek, basitçe bir sonraki satırdaki tüm hataları bastıran // @ts-ignore'u (TS 2.6'da eklendi) kullanmaktır:
// @ts-ignore
thing._name = "Unit Test";
Bunun sorunu şu ki, bu, sonraki satırdaki tüm hataları baskılıyor:
// @ts-ignore
thing._name(123).this.should.NOT.beAllowed("but it is") = window / {};
Ben şahsen @ts-ignore'u bir kod kokusu olarak değerlendiriyorum ve belgelerin dediği gibi:
bu yorumları çok sınırlı bir şekilde kullanmanızı öneririz. [orijinal vurgulama]
Cevap #2
Özel yöntemleri ÇAĞIRABİLİRSİN!
Aşağıdaki hatayla karşılaştıysanız:
expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/)
// TS2341: Property 'initFooBar' is private and only accessible within class 'FooBar'
sadece // @ts-ignore kullan:
// @ts-ignore
expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/)
@Moff452'ye yorum için teşekkürler. Ayrıca şunları da yazabilirsiniz:
expect(new FooBar(/*...*/)['initFooBar']()).toEqual(/*...*/)
GÜNCELLEME :
@ts-expect-error, @ts-ignore için daha iyi bir alternatiftir. Bkz: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#ts-ignore-or-ts-expect-error
Cevap #3
Bu benim için işe yaradı:
Bunun yerine:
sut.myPrivateMethod();
Bu :
sut['myPrivateMethod']();
Cevap #4
Çoğu geliştirici özel fonksiyonları test etmeyi önermediği için, neden test etmiyorsunuz?
Örn.
SeninSinifin.ts
export class FooBar {
private _status: number;
constructor( private foo : Bar ) {
this.initFooBar({});
}
private initFooBar(data){
this.foo.bar( data );
this._status = this.foo.foo();
}
}
TestYourClass.spec.ts
describe("Testing foo bar for status being set", function() {
...
//Variable with type any
let fooBar;
fooBar = new FooBar();
...
//Method 1
//Now this will be visible
fooBar.initFooBar();
//Method 2
//This doesn't require variable with any type
fooBar['initFooBar']();
...
}
@Aaron'a, @Thierry Templier'e teşekkürler.
Cevap #5
Özel yöntemi köşeli parantez kullanarak çağırın.
Ts dosyası
class Calculate{
private total;
private add(a: number) {
return a + total;
}
}
spect.ts dosyası
it('should return 5 if input 3 and 2', () => {
component['total'] = 2;
let result = component['add'](3);
expect(result).toEqual(5);
});
