前一篇我們測試簡單元件上的屬性,今天用component搭配service一起做個簡單的測試
今日學習目標以理解如何測試service為主,所以延續前一個 UserComponent.html UI,這次我們增加isLoggedIn 的變數,以及新增一個service,稍微調整component,然後在寫spec測試寫的內容有沒有符合預期。
isLoggedIn 和 !isLoggedIn 來測試登入和未登入狀態
<div *ngIf="isLoggedIn">
  <h1>User logged in</h1>
  <p>User is: {{ user.name }}</p>
</div>
<div *ngIf="!isLoggedIn">
  <h1>User not logged in</h1>
  <p>Please log in first</p>
</div>
ng g s user ,然後只寫一個user的物件,可以從外部取得name
line 18

export class UserComponent implements OnInit {
  
  user: {name: string} = { name: ''};
  isLoggedIn = false;
  constructor(private userService: UserService) { }
  ngOnInit(): void {
    this.user = this.userService.user;
  }
}
line 2, line 3 建立與取得測試用元件UserComponent,取得元件後,從 fixture 透過injector取得 UserService,因為DOM會動態更新,避免測試瀏覽器在run的時候,拿到undefined的變數 user.name ,所以要加上 line 9去偵測更新DOM。最後line 10 預期userService的user name == UserComponent上的user nameit('should use the user name from the service', () => {
    let fixture = TestBed.createComponent(UserComponent);
    let component = fixture.componentInstance;
    
    // 這行是重點!!
    let userService = fixture.debugElement.injector.get(UserService);
    
    // 避免拿到undefined的property
    fixture.detectChanges();
    expect(userService.user.name).toEqual(component.user.name);
});

這次要測試用isLoggedIn的變數,假設登入狀況,所以 line 2, line 3 建立與取得元件後,設定UserComponent的isLoggedIn為true,也就是 line 5,一樣避免在環境還沒run完全之前拿到undefined的變數,需要偵測DOM的狀態,加入 line 7,那接著宣告一個變數compiled來取得DOM上面的元素,最後看元素的內容有沒有元件裡名字。
it('should display the user name if user is logged in', () => {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.componentInstance;
    // 測試登入情境
    app.isLoggedIn = true;
    
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    
    expect(compiled.querySelector('p').textContent).toContain(app.user.name)
});

有登入成功的測試情境,反之就有「未登入」的情境,這次把isLoggedIn拿掉,應該就會測試成功了吧?!
it('should\'t display the user name if user is not logged in', () => {
    let fixture = TestBed.createComponent(UserComponent);
    let app = fixture.componentInstance;
    fixture.detectChanges();
    let compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('p').textContent).toContain(app.user.name)
 });
看看上面寫的Expected 'Please log in first' to contain 'Chris'.,因為我們要測試的是「未登入」狀態,所以測試的地方要改某一行讓它pass

expect(compiled.querySelector('p').textContent).not.toContain(app.user.name)
因為未登入,就不會取得user.name
<!-- 已登入 -->
<div *ngIf="isLoggedIn">
  <h1>User logged in</h1>
  <p>User is: {{ user.name }}</p>
</div>
<!-- 未登入 -->
<div *ngIf="!isLoggedIn">
  <h1>User not logged in</h1>
  <p>Please log in first</p>
</div>

認識如何取得service和調用service裡面的參數,以及要測試正面和反面的情境,各自寫一個test case,對於測試和實做有大大幫助,最後在斷言庫的地方,還好有測試反面情境,認識了可以加 not 的方法,多學了一種使用方式。
下一篇,要來學習「Async」和「fakeAsync」