終於來到 TS 最後一篇測試範例了,今天要來認識 detector change 的測試方法
課程名稱如下:
我們先來看看,原始元件 typescript 檔案
Change Detector 元件,changeDetecor,是當元件上的值有變化時會自動更新 UI,我們可以將 change detector 抽出來讓 UI 不再自動更新,或者透過手動呼叫 detect change function 更新元件。可是,change detector 和其他一般 service 不同,沒辦法 inject 到 testbed做測試,但是元件本身呼叫 detectChange 來測試。
Functions In The Constructor 元件,constructor 是在元件建立後第一個執行的地方,所以沒辦法手動呼叫 constructor 中的 service,為了解決這個問題,我們需要建立新的元件,在透過新元件呼叫 constructor 的服務。
Change Detector In The Constructor 元件,這個測試案例是結合 detector change 和 contrustor 的方式進行,因為無法在元件建立後測試 constructor,所以需要用新元件呼叫,細節留到後面 spec 說明。
來看看,對應的 Spec 檔案,configureTestingModule 一樣取代成測試用的元件
beforeEach(waitForAsync(() => {
configureTestingModule({
declarations: [
TestingTheChangeDetectorComponent
],
}).compileComponents();
}));
changeDetecor 是 private 參數,所以用 key 的方式取得 line 5
,透過 spyOn 取得 detectorChanges function,然後透過呼叫元件的 ngOnInit 測試變更。
it('should update the UI after loading the data', () => {
//Assign
// const changeDetector = TestBed.inject(ChangeDetectorRef); // Won't work
const changeDetector = component['changeDetector'];
const detectChangesSpy = spyOn(changeDetector, 'detectChanges');
// This also works, but avoid using prototype if you don't need to
// const changeDetector = componentFixture.debugElement.injector.get(ChangeDetectorRef);
// const detectChangesSpy = spyOn(changeDetector.constructor.prototype, 'detectChanges');
//Act
component.ngOnInit();
//Assert
expect(detectChangesSpy).toHaveBeenCalledWith();
});
beforeEach(waitForAsync(() => {
configureTestingModule({
declarations: [
TestingFunctionsInTheConstructorComponent
],
}).compileComponents();
}));
前面有提到,因為沒辦法用手動呼叫 constructor,需要透過「建立新元件」trigger contructor,由於在建立新元件前就spy ApiService,讓我們可以檢查 function getData
it('should get the data from the api on construction', () => {
//Assign
const apiService = TestBed.inject(ApiService);
spyOn(apiService,'getData');
//Act
TestBed.createComponent(TestingFunctionsInTheConstructorComponent);
//Assert
expect(apiService.getData).toHaveBeenCalledWith();
});
beforeEach(waitForAsync(() => {
configureTestingModule({
declarations: [
TestingTheChangeDetectorInTheConstructorComponent
],
}).compileComponents();
}));
這邊是結合上述兩種測例,但是我們無法用建立新元件方式 trigger constructor,需要調用 prototype 的 instance 來使用
it('should detach from the change detector on construction', () => {
//Assign
// const changeDetector = TestBed.inject(ChangeDetectorRef); // Won't work
// This won't work either as we create a new component
// const changeDetector = component['changeDetector'];
// const detectChangesSpy = spyOn(changeDetector, 'detectChanges');
// This won't work either
// const detectChangesSpy = spyOn(ChangeDetectorRef.prototype, 'detach');
const changeDetector = componentFixture.debugElement.injector.get(ChangeDetectorRef);
const detectChangesSpy = spyOn(changeDetector.constructor.prototype, 'detach');
//Act
TestBed.createComponent(TestingTheChangeDetectorInTheConstructorComponent);
//Assert
expect(detectChangesSpy).toHaveBeenCalledWith();
});