如何在 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 單元測試的難點在於該如何解決
RouterDI 注入,所幸搭配RouterTestingModule,就可以幫我們解決 RouterTestingModule所提供的方式不算 mockRouter,只是幫我們解決了RouterDI 注入問題,不管如何,能夠在不執行ng serve就可執行測試,就非常難得
Sample Code
完整的範例可以在我的 GitHub 上找到
Reference
Angular, Testing
Asim, Testing Routing
Burak Tasci, Using Jasmine framework to test Angular Router