iT邦幫忙

2022 iThome 鐵人賽

DAY 9
0
Modern Web

Angular TDD (Test-driven development ) 從0到1系列 第 9

Angular TDD 測試從0到1: Day 9 Service 測試

  • 分享至 

  • xImage
  •  

前一篇我們測試簡單元件上的屬性,今天用component搭配service一起做個簡單的測試

今日學習目標以理解如何測試service為主,所以延續前一個 UserComponent.html UI,這次我們增加isLoggedIn 的變數,以及新增一個service,稍微調整component,然後在寫spec測試寫的內容有沒有符合預期。

  • 第一步,html增加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>
  • 新增UserService, ng g s user ,然後只寫一個user的物件,可以從外部取得name

  • component 同步改成從service取得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;
  }
}

接著我們寫測試案例(1)

  • line 2, line 3 建立與取得測試用元件UserComponent,取得元件後,從 fixture 透過injector取得 UserService,因為DOM會動態更新,避免測試瀏覽器在run的時候,拿到undefined的變數 user.name ,所以要加上 line 9去偵測更新DOM。最後line 10 預期userService的user name == UserComponent上的user name
it('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);
});
  • 測試結果

測試Service (2)

這次要測試用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)
});
  • 測試結果

測試Service (3)

有登入成功的測試情境,反之就有「未登入」的情境,這次把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)
 });
  • 測試結果,fall了!

看看上面寫的Expected 'Please log in first' to contain 'Chris'.,因為我們要測試的是「未登入」狀態,所以測試的地方要改某一行讓它pass

  • 測試修改這行,看到多了什麼嗎? ==not==
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」


上一篇
Angular TDD 測試從0到1: Day 8 Component 測試
下一篇
Angular TDD 測試從0到1: Day 10 Async vs fakeAsync 測試
系列文
Angular TDD (Test-driven development ) 從0到130
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言