前一篇是好理解的 UI、CSS 測試,今天要來學習如何測試「directive」的情境。也就是「ngIf」、「ngFor」、「ngClass」,直接來看範例
在寫測試案例時,不只正向要測,反向也要測試,才不會有落高的情境沒被測到。
元件上,一個用參數,一個用函式當作 ngIf
的條件,當條件成立才會顯示裡面的 div
。
<ng-container *ngIf="showTitle">
<div id="title1">First Title</div>
</ng-container>
<ng-container *ngIf="showTitleFunction()">
<div id="title2">Second Title</div>
</ng-container>
showTitle
= true,斷言的地方取得 id 為 title1
,預期該element須存在。showTitle
= false,斷言的地方取得方式一樣,但要預期不該存在,所以用 toBeFalsy
。title2
,預期要存在。toBeFalsy
。 it('should show the title when showTitle is true', () => {
//Assign
component.showTitle = true;
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title1'));
expect(element).toBeTruthy();
});
it('shouldnt show the title when showTitle is false', () => {
//Assign
component.showTitle = false;
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title1'));
expect(element).toBeFalsy();
});
it('should show the title when showTitleFunction returns true', () => {
//Assign
spyOn(component,'showTitleFunction').and.returnValue(true);
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title2'));
expect(element).toBeTruthy();
});
it('shouldnt show the title when showTitleFunction returns false', () => {
//Assign
spyOn(component,'showTitleFunction').and.returnValue(false);
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.query(By.css('#title2'));
expect(element).toBeFalsy();
});
ngFor
可以用for迴圈的方式將array的值一個一個顯示出來,取決於array內的物件形式顯示對應的值。
<ng-container *ngFor="let message of messages">
<div class="row">{{message}}</div>
</ng-container>
export class TestingNgForsComponent {
title = 'example-angular-app';
messages = ['string 1','string 2'];
}
messages
參數,查詢 fixutre debugElement 有沒有名為 row 的 class,該元素長度要為3。component.messages
而不是 fixture 來跑迴圈檢查值。describe('queryAll', () => {
it('should show the correct number of elements', () => {
//Assign
component.messages = ['a','b','c'];
//Act
componentFixture.detectChanges();
//Assert
const elements = componentFixture.debugElement.queryAll(By.css('.row'));
expect(elements.length).toEqual(3);
});
it('should pass down the correct data to its child components', () => {
//Assign
component.messages = ['a','b','c'];
//Act
componentFixture.detectChanges();
//Assert
const elements = componentFixture.debugElement.queryAll(By.css('.row'));
for (let i = 0; i < component.messages.length; i++) {
expect(elements[i].nativeElement.innerText).toEqual(component.messages[i]);
}
// Do not loop over the elements
// for (let i = 0; i < elements.length; i++) {
// expect(elements[i].nativeElement.innerText).toEqual(component.messages[i]);
// }
});
});
常用在不同的元件行為,要有不同的UI呈現,舉例來說:按鈕狀態可以被點選的時候 class="active",當按鈕狀態不符條件需要 disabled,class="inactive",ngClass
可以動態的根據資料狀態,檢核條件是否成立,成立才使用對應的 class 名稱。
<div id="title1" [ngClass]="{'active': activeTitle}">First Title</div>
<div id="title2" [ngClass]="{'active': activeTitleFunction()}">Second Title</div>
export class TestingNgClassComponent {
title = 'example-angular-app';
public activeTitle = false;
public activeTitleFunction() { return true; }
}
querySelector
方式,不同的地方在 line 11
用 toContain
判斷元素上是否有對應的 class 名稱 active
。describe('querySelector', () => {
it('should apply the class active when activeTitle is true', () => {
//Assign
component.activeTitle = true;
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.nativeElement.querySelector('#title1');
expect(element.classList).toContain('active');
});
it('shouldnt apply the class active when activeTitleFunction returns false', () => {
//Assign
spyOn(component,'activeTitleFunction').and.returnValue(false);
//Act
componentFixture.detectChanges();
//Assert
const element = componentFixture.debugElement.nativeElement.querySelector('#title2');
expect(element.classList).not.toContain('active');
});
})
querySelectorAll
取元素line 11
取得元素後的第一個 index 是否有對應的 class 名稱。describe('querySelectorAll', () => {
it('should apply the class active when activeTitle is true', () => {
//Assign
component.activeTitle = true;
//Act
componentFixture.detectChanges();
//Assert
const elements = componentFixture.debugElement.nativeElement.querySelectorAll('#title1');
expect(elements[0].classList).toContain('active');
});
})
明天邁向 HTML 測試最後一篇,雖然今天的範例很簡單很好理解,如果 DOM 元素操作很熟悉的讀者相信很快就能吸收,如果新手讀者的話,希望這邊可以當作範例工具文章,理解常見的 directive 測試方式。