前言
也有一種狀況是:依賴的服務元件資料的取得,是透過呼叫 API 等待伺服器吐資料的非同步行為,那麼這又該如何進行測試呢?
帶有非同步服務的元件
改寫上一個範例,當點選 welcomeComponent 元件內的登入按鈕時,會以 .subscribe()
的形式觸發 user 服務元件的 getData()
方法取得資料,最後顯示出歡迎提示,並且停用登入按鈕。
welcome.component 的 Template
1 | <h3 class="welcome" *ngIf="data.isLoggedIn"><i>{{data.user}},{{data.message}}!</i></h3> |
welcome.component 的 class
1 | import { Component, OnInit } from '@angular/core'; |
user.service
1 | import { Injectable } from '@angular/core'; |
如何測試帶有非同步服務的元件
這個範例測試的重點是:
- 元件上的登入按鈕的 click 觸發事件是否有效
- 意思是當透過點擊事件觸發元件內的
login()
時,方法真的有被呼叫
- 意思是當透過點擊事件觸發元件內的
- 元件的渲染是不是正常的
- 意思是獲得資料後元件有正確的顯示
測試環境建置
於是我們可以像先前一樣,使用 jasmine.createSpyObj()
產生一個假的 getData()
方法,並且預先建立好一組假的資料 - data
。
welcome.component.spec
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
撰寫測試
前置作業準備完畢,開始撰寫測試吧。
第一個測試
- 元件上的登入按鈕的 click 觸發事件是否有效
- 意思是當透過點擊事件觸發元件內的
login()
時,方法真的有被呼叫
- 意思是當透過點擊事件觸發元件內的
1 | it('觸發 Click 事件後,是否有正常呼叫 getData() ', () => { |
第二行的意思是,當假的 getData
方法被呼叫時必須回傳一個觀察者物件 (Observable) 。
因為第三行觸發元件內的 login
方法時
- user 服務元件內的
getData()
被觸發了,並且使用.subscribe()
方法訂閱 - 而我們在測試時使用的
getData()
是假造的替身,所以必須回傳一個觀察者物件才可以使用.subscribe()
,否則會出錯導致測試失敗
第二個測試
- 測試元件的渲染是正常的
- 意思是獲得資料後元件有正確的顯示
- 像是當
isLoggedIn
為true
時,登入按鈕為disabled
- 像是當
- 意思是獲得資料後元件有正確的顯示
1 | it('未登入時元件的渲染', () => { |
而這部分測試的關鍵在於「順序」,像是:
component.data = data;
當我們把假資料重新賦值給元件內的data
後- 必須呼叫
fixture.detectChanges();
重新進行資料與元件間的繫結- 如此才可以使用
fixture.nativeElement.querySelector()
找到指定目標
- 如此才可以使用
- 必須呼叫
fakeAsync() 進行非同步測試
在這個範例內,我並沒有實際的呼叫 API ,而是在 user 服務元件內透過 setTimeout()
模擬呼叫 API 時等待伺服器的時間。
而如果也想在測試過程中模擬這一段情境的話,可以使用 fakeAsync()
。
1 | it('非同步測試渲染情形', fakeAsync(() => { |
這個測試使用了 setTimeout()
方法,令 data
物件內的屬性三秒後變更,並且重新賦值給元件內的 data
屬性,最後重新繫結。
而這個測試另一個關鍵是 tick() , tick()
函式接受一個毫秒值作為參數(如果沒有提供則預設為 0)。
該參數表示虛擬時鐘要前進多少,也就是說:
setTimeout()
如果時間設置 3000 ,那麼tick()
也要設置 3000
這樣才能正確取得資料。
至此完成了對元件的測試。