如何在 Angular 對 Route 單元測試 ?
前後端分離後,Angular 有自己的 route,在 component 也必須處理 route。由於 component 是 DI 注入 Router
,因此若要對 route 單元測試,首先必須要解決的就是 Router
注入問題 。
Version
Node.js 8.9.4
Angular CLI 1.6.7
Angular 5.2.3
User Story
首頁使用的是 PostComponent
,按下 Redirect to comment
將會導到 /comment
。
導到 /comment
之後,將顯示 CommentComponent
。
Task
對 PostComponent
單元測試,確定有正確導到 /comment
。
Implementation
app-routing.module.ts
1 | import { NgModule } from '@angular/core'; |
第 6 行
1 | export const routes: Routes = [ |
實際的 route 設定,為了單元測試也能使用,所以特別使用 export
。
post.component.html
1 | <p>post works!</p> |
當按下 Redirect to comment
時,會執行 onRedirectClick()
。
post.component.ts
1 | import { Component } from '@angular/core'; |
10 行
1 | constructor(private router: Router) { |
要使用 Router
去切換 route,因此 DI 注入 Router
。
13 行
1 | onRedirectClick() { |
使用 Router.navigateByUrl()
切換到 comment
。
post.component.spec.ts
1 | import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; |
15 行
1 | imports: [ |
Angular 對於 route 單元測試,另外提供了 RouterTestingModule
,使用 withRoutes()
將原本的 route 帶入。
18 行
1 | declarations: [ |
由於我們由 RouterTestingModule.withRoutes()
所載入的 route 包含 PostComponent
與 CommentComponent
,因此需要特別在 declarations
宣告。
11 行
1 | let router: Router; |
宣告要測試的 router
。
由於其型別仍然是
Router
,而不是如HttpClient
的HttpTestingController
,所以不算 mock,RouterTestingModule
只是幫我們解決了 DI 的問題
27 行
1 | router = TestBed.get(Router); |
由 DI 幫我們建立 router
物件。
34 行
1 | it(`should navigate to comment`, fakeAsync(() => { |
這裡使用了 fackAsync()
與 tick()
,這是 Angular 提供的非同步測試機制。
主要的原因是 Router.navigateByUrl()
回傳的是 Promise<boolean>
,也就是非同步,因此必須使用 fackAsync()
與 tick()
。
簡單的說,由於在 onRedirectClick()
的 Router.navigateByUrl()
為非同步,我們必須使用 tick()
等待非同步執行完畢後,才能繼續執行 expect()
,tick()
讓我們不用寫 callback,以更同步的寫法來測試非同步行為。
最後直接 expect()
router.url
,測試目前 url 有沒有如預期。
使用 Wallaby.js 通過所有 route 單元測試。
Conclusion
- Route 單元測試的可貴在於不用執行
ng serve
,就可以測試 route 是否正確 - Route 單元測試的難點在於該如何解決
Router
DI 注入,所幸搭配RouterTestingModule
,就可以幫我們解決 RouterTestingModule
所提供的方式不算 mockRouter
,只是幫我們解決了Router
DI 注入問題,不管如何,能夠在不執行ng serve
就可執行測試,就非常難得
Sample Code
完整的範例可以在我的 GitHub 上找到
Reference
Angular, Testing
Asim, Testing Routing
Burak Tasci, Using Jasmine framework to test Angular Router