$npx -y skills add kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-advanced-webapi-integration-testingASP.NET Core WebApi 整合測試完整指南。當需要對 WebApi 端點進行整合測試或驗證 ProblemDetails 錯誤格式時使用。涵蓋 WebApplicationFactory、IExceptionHandler、Testcontainers 多容器編排、Flurl URL 建構與 AwesomeAssertions HTTP 驗證。 Make sure to use this skill whenever the user mentions WebApi integration testing, ProblemDetails,
| 1 | # WebApi 整合測試 |
| 2 | |
| 3 | ## 學習目標 |
| 4 | |
| 5 | 完成本技能學習後,您將能夠: |
| 6 | |
| 7 | 1. 建立完整的 WebApi 整合測試架構 |
| 8 | 2. 使用 `IExceptionHandler` 實作現代化異常處理 |
| 9 | 3. 驗證 `ProblemDetails` 和 `ValidationProblemDetails` 標準格式 |
| 10 | 4. 使用 Flurl 簡化 HTTP 測試的 URL 建構 |
| 11 | 5. 使用 AwesomeAssertions 進行精確的 HTTP 回應驗證 |
| 12 | 6. 建立多容器 (PostgreSQL + Redis) 測試環境 |
| 13 | |
| 14 | ## 核心概念 |
| 15 | |
| 16 | ### IExceptionHandler - 現代化異常處理 |
| 17 | |
| 18 | ASP.NET Core 8+ 引入的 `IExceptionHandler` 介面提供了比傳統 middleware 更優雅的錯誤處理方式。`GlobalExceptionHandler` 依據例外類型(KeyNotFoundException → 404、ArgumentException → 400、其他 → 500)產生對應的 `ProblemDetails` 回應。 |
| 19 | |
| 20 | ### ProblemDetails 標準格式 |
| 21 | |
| 22 | RFC 7807 定義的統一錯誤回應格式: |
| 23 | |
| 24 | | 欄位 | 說明 | |
| 25 | | ---------- | ------------------ | |
| 26 | | `type` | 問題類型的 URI | |
| 27 | | `title` | 簡短的錯誤描述 | |
| 28 | | `status` | HTTP 狀態碼 | |
| 29 | | `detail` | 詳細的錯誤說明 | |
| 30 | | `instance` | 發生問題的實例 URI | |
| 31 | |
| 32 | ### ValidationProblemDetails - 驗證錯誤專用 |
| 33 | |
| 34 | 繼承自 ProblemDetails,額外包含 `errors` 字典,記錄每個欄位的驗證錯誤訊息。 |
| 35 | |
| 36 | ### FluentValidation 異常處理器 |
| 37 | |
| 38 | FluentValidation 異常處理器實作 `IExceptionHandler` 介面,專門處理 `ValidationException`,將驗證錯誤轉換為標準的 `ValidationProblemDetails` 格式回應。處理器之間按照註冊順序執行,特定處理器(如 FluentValidation)必須在全域處理器之前註冊。 |
| 39 | |
| 40 | > 完整實作程式碼請參閱 [references/exception-handler-details.md](references/exception-handler-details.md) |
| 41 | |
| 42 | ## 整合測試基礎設施 |
| 43 | |
| 44 | 測試基礎設施由三個核心組件構成: |
| 45 | |
| 46 | - **TestWebApplicationFactory**:繼承 `WebApplicationFactory<Program>`,配置多容器(PostgreSQL + Redis)與 DI 替換(如 FakeTimeProvider) |
| 47 | - **IntegrationTestCollection**:Collection Fixture 定義,確保容器共享 |
| 48 | - **IntegrationTestBase**:測試基底類別,提供 HttpClient、DatabaseManager、FlurlClient 與時間控制方法 |
| 49 | |
| 50 | > 完整基礎設施程式碼請參閱 [references/test-infrastructure.md](references/test-infrastructure.md) |
| 51 | |
| 52 | ## Flurl 簡化 URL 建構 |
| 53 | |
| 54 | ```csharp |
| 55 | // 傳統方式 |
| 56 | var url = $"/products?pageSize={pageSize}&page={page}&keyword={keyword}"; |
| 57 | |
| 58 | // 使用 Flurl |
| 59 | var url = "/products" |
| 60 | .SetQueryParam("pageSize", 5) |
| 61 | .SetQueryParam("page", 2) |
| 62 | .SetQueryParam("keyword", "特殊"); |
| 63 | ``` |
| 64 | |
| 65 | ## 測試範例 |
| 66 | |
| 67 | 涵蓋成功建立產品(201 Created)、驗證錯誤(400 BadRequest + ValidationProblemDetails)、資源不存在(404 NotFound + ProblemDetails)、分頁查詢(200 OK + PagedResult)等完整測試範例,以及 TestHelpers 資料管理策略。 |
| 68 | |
| 69 | > 完整測試範例與資料管理程式碼請參閱 [references/test-examples.md](references/test-examples.md) |
| 70 | |
| 71 | ## 最佳實務 |
| 72 | |
| 73 | ### 1. 測試結構設計 |
| 74 | |
| 75 | - **單一職責**:每個測試專注於一個特定場景 |
| 76 | - **3A 模式**:清楚區分 Arrange、Act、Assert |
| 77 | - **清晰命名**:方法名稱表達測試意圖 |
| 78 | |
| 79 | ### 2. 錯誤處理驗證 |
| 80 | |
| 81 | - **ValidationProblemDetails**:驗證錯誤回應格式 |
| 82 | - **ProblemDetails**:驗證業務異常回應 |
| 83 | - **HTTP 狀態碼**:確認正確的狀態碼 |
| 84 | |
| 85 | ### 3. 效能考量 |
| 86 | |
| 87 | - **容器共享**:使用 Collection Fixture |
| 88 | - **資料清理**:測試後清理資料,不重建容器 |
| 89 | - **並行執行**:確保測試獨立性 |
| 90 | |
| 91 | ## 相依套件 |
| 92 | |
| 93 | ```xml |
| 94 | <PackageReference Include="xunit" Version="2.9.3" /> |
| 95 | <PackageReference Include="AwesomeAssertions" Version="9.4.0" /> |
| 96 | <PackageReference Include="Testcontainers.PostgreSql" Version="4.11.0" /> |
| 97 | <PackageReference Include="Testcontainers.Redis" Version="4.11.0" /> |
| 98 | <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.0" /> |
| 99 | <PackageReference Include="Flurl" Version="4.0.0" /> |
| 100 | <PackageReference Include="Respawn" Version="7.0.0" /> |
| 101 | ``` |
| 102 | |
| 103 | ## 專案結構 |
| 104 | |
| 105 | ```text |
| 106 | src/ |
| 107 | ├── Api/ # WebApi 層 |
| 108 | ├── Application/ # 應用服務層 |
| 109 | ├── Domain/ # 領域模型 |
| 110 | └── Infrastructure/ # 基礎設施層 |
| 111 | tests/ |
| 112 | └── Integration/ |
| 113 | ├── Fixtures/ |
| 114 | │ ├── TestWebApplicationFactory.cs |
| 115 | │ ├── IntegrationTestCollection.cs |
| 116 | │ └── IntegrationTestBase.cs |
| 117 | ├── Handlers/ |
| 118 | │ ├── GlobalExceptionHandler.cs |
| 119 | │ └── FluentValidationExceptionHandler.cs |
| 120 | ├── Helpers/ |
| 121 | │ ├── DatabaseManager.cs |
| 122 | │ └── TestHelpers.cs |
| 123 | ├── SqlScripts/ |
| 124 | │ └── Tables/ |
| 125 | └── Controllers/ |
| 126 | └── ProductsControllerTests.cs |
| 127 | ``` |
| 128 | |
| 129 | ## 輸出格式 |
| 130 | |
| 131 | - 產生 `TestWebApplicationFactory.cs`,配置多容器(PostgreSQL + Redis)與 DI 替換 |
| 132 | - 產生 `IntegrationTestCollection.cs` 與 `IntegrationTestBase.cs` 測試基礎設施 |
| 133 | - 產生 `DatabaseManager.cs`,整合 Respawn 進行測試資料清理 |
| 134 | - 產生控制器測試類別,驗證 CRUD、ProblemDetails 與 ValidationProblemDetails |
| 135 | - 產生 `GlobalExceptionHandler.cs` 與 `FluentValidationExceptionHandler.cs` 異常處理器 |
| 136 | |
| 137 | ## 參考資源 |
| 138 | |
| 139 | ### 原始文章 |
| 140 | |
| 141 | 本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章: |
| 142 | |
| 143 | - **Day 23 - 整合測試實戰:WebApi 服務的整合測試** |
| 144 | - 鐵人賽文章:https://ithelp.ithome.com.tw/articles/10376873 |
| 145 | - 範例程式碼:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day23 |
| 146 | |
| 147 | ### 官方文件 |
| 148 | |
| 149 | - [ASP.NET Core 整合測試](https://docs.microsoft.com/aspnet/core/test/integration-tests) |
| 150 | - [IExceptionHandler 文件](https://learn.microsoft.com/aspnet/core/fundamentals/error-handling) |
| 151 | - [ProblemD |