﻿# 整理幾個 Angular 單元測試的學習資源與注意事項

最近嘗試在 Angular 的「獨立元件」架構下撰寫單元測試，發現有些技巧官方文件沒寫清楚的問題，需要特別注意。除此之外，這篇文章我還打算整理一些學習資源給大家參考。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/adb0c906d5eb3732d216-638b7ab7-e426-41f2-8331-164333de8a44.webp)

### 鬼打牆的 `provideHttpClientTesting()` 用法

由於從 Angular 17 開始，透過 `ng new` 建立的專案就是以「獨立元件」架構為主，而大部分的 Angular 模組也都看不到了，我原本想說在寫單元測試的時候，應該也會是相同的架構，但看官方文件的時候，否而被誤導了，以下我就說明一下我這次鬼打牆的過程。

首先，我在 AppComponent 注入一個 HttpClient 服務，因此我要在 `app.config.ts` 加入 `provideHttpClient()` 提供者設定：

```typescript
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient()
  ]
};
```

接著，我在 `app.component.spec.ts` 中調整 `TestBed` 的設定 (**注意**: 這是錯誤的設定)：

```typescript
await TestBed.configureTestingModule({
  imports: [AppComponent],
  providers: [
    provideHttpClientTesting(),
  ]
}).compileComponents();
```

我為什麼會這樣寫呢？因為新版 [Test requests](https://angular.dev/guide/http/testing) 文件的 [Setup for testing](https://angular.dev/guide/http/testing#setup-for-testing) 就是這樣給範例的！

```typescript
TestBed.configureTestingModule({
  providers: [
    // ... other test providers
    provideHttpClientTesting(),
  ],
});
const httpTesting = TestBed.inject(HttpTestingController);
```

我壓根沒想到，在 `app.component.spec.ts` 中還需要註冊 `provideHttpClient()` 提供者，為什麼沒想到呢？因為在舊版 [Test requests](https://angular.io/guide/http-test-requests#setup-for-testing) 文件中的 [Setup for testing](https://angular.io/guide/http-test-requests#setup-for-testing) 小節，是這樣宣告的：

```typescript
TestBed.configureTestingModule({
  imports: [ HttpClientTestingModule ]
});
```

以前就不用特別匯入 `HttpClientModule` 了，對吧！所以到了獨立元件架構，應該也不用註冊 `provideHttpClient()` 提供者不是嗎？

我錯了！

正確的宣告方法如下：

```typescript
await TestBed.configureTestingModule({
  imports: [AppComponent],
  providers: [
    provideHttpClient(),
    provideHttpClientTesting(),
  ]
}).compileComponents();
```

這樣才能正確的宣告單元測試中 `TestBed` 所需的測試模組。我真的覺得這點實在是太雷了！

最氣人的點是，舊式的寫法完全可以跑，也就是說，以下這段 Code 也是完全正確的寫法：

```typescript
TestBed.configureTestingModule({
  imports: [ HttpClientTestingModule ]
});
```

簡單來說，我要是不要雞婆的一定要用新的「獨立元件」架構下的風格來寫「單元測試」，那就完全不會踩到地雷，我就是完全被「新版文件」給誤導了！😅

### 學習資源

以下我列出幾個我覺得不錯的學習資源，這對想開始寫 Angular 單元測試的人應該很有幫助：

1.  如果你用 Angular 17 的話，還是建議你直接看新版 [Angular.dev](https://angular.dev/) 網站上的 [Testing](https://angular.dev/guide/testing) 文件！
    
    只要注意本文上方提到的地雷即可。
    
2.  如果你並非使用「獨立元件」架構，其實你直接看舊版 [Angular.io](https://angular.io/) 網站下的 [Testing](https://angular.io/guide/testing) 文件即可，這是完全沒問題的！
    
    舊版文件也可以看 [angular.tw](https://angular.io/) 的正體中文的 [測試](https://angular.io/guide/testing) 文件，整個系列的文件看完，基本上很容易就能上手。
    
3.  新手上路最缺的就是「範例程式」了，只要有「範例程式」可以看，你隨時都可以用「複製/貼上」的方式來開發單元測試的程式碼。
    
    我真的不是開玩笑，要開始寫單元測試時，很多時候寫不出來，就是去看別人的程式碼，然後「複製/貼上」，然後「修改」，這樣才能快速上手。
    
    我推薦去下載 Angular 在 GitHub Repo 上的 [Testing](https://github.com/angular/angular/tree/main/packages/examples/testing) 範例程式，這裡有相當完整的範例程式可供參考，還有一些撰寫非同步程式時的工具函式可用，例如 [asyncData](https://github.com/angular/angular/blob/7220c89cef39b690246a856cad7f82071d2a2906/aio/content/examples/testing/src/testing/async-observable-helpers.ts#L20-L22) 或 [asyncError](https://github.com/angular/angular/blob/7220c89cef39b690246a856cad7f82071d2a2906/aio/content/examples/testing/src/testing/async-observable-helpers.ts#L30-L32) 之類的，都可以從這份範例程式找到。
    
4.  推薦一本免費的 [Testing Angular](https://testing-angular.com/) 電子書 (英文)，整本書的內容都可以線上瀏覽，還可以下載 EPUB 格式的電子書回去離線閱讀。
    
5.  最後，當然是不要忘記 [Angular Taiwan](https://fb.angular.tw/) 臉書社團，有問題都可以來這裡交流，還有 台灣 Angular 技術論壇 也可以來這裡進行更深入的討論。
    

### 相關連結

-   新版官網的 [Testing](https://angular.dev/guide/testing) 文件
-   舊版官網的 [Testing](https://angular.io/guide/testing) 文件
-   舊版官網的 [Testing](https://angular.tw/guide/testing) 文件 (正體中文版)
-   免費電子書: [Testing Angular](https://testing-angular.com/)
