$npx -y skills add kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-advanced-aspnet-integration-testingASP.NET Core 整合測試的專門技能。當需要測試 Web API 端點、HTTP 請求/回應、中介軟體、依賴注入時使用。涵蓋 WebApplicationFactory、TestServer、HttpClient 測試、記憶體資料庫配置等。 Make sure to use this skill whenever the user mentions ASP.NET Core integration testing, WebApplicationFactory, TestServer, HTTP endpoint testing, or middl
| 1 | # ASP.NET Core 整合測試指南 |
| 2 | |
| 3 | ## 核心概念 |
| 4 | |
| 5 | ### 整合測試的兩種定義 |
| 6 | |
| 7 | 1. **多物件協作測試** — 將兩個以上的類別做整合,測試它們之間的運作是否正確 |
| 8 | 2. **外部資源整合測試** — 使用到資料庫、外部服務、檔案等外部資源的測試 |
| 9 | |
| 10 | ### 為什麼需要整合測試? |
| 11 | |
| 12 | - 確保多個模組整合後能正確工作 |
| 13 | - 單元測試無法涵蓋的整合點:Routing、Middleware、Request/Response Pipeline |
| 14 | - 確認 WebApplication 的整合與設定是否正確 |
| 15 | - 確認異常處理是否完善 |
| 16 | |
| 17 | ### 測試金字塔定位 |
| 18 | |
| 19 | | 測試類型 | 測試範圍 | 執行速度 | 維護成本 | 建議比例 | |
| 20 | | ---------- | ------------- | -------- | -------- | -------- | |
| 21 | | 單元測試 | 單一類別/方法 | 很快 | 低 | 70% | |
| 22 | | 整合測試 | 多個元件 | 中等 | 中等 | 20% | |
| 23 | | 端對端測試 | 完整流程 | 慢 | 高 | 10% | |
| 24 | |
| 25 | ## 原則一:使用 WebApplicationFactory 建立測試環境 |
| 26 | |
| 27 | 透過 `WebApplicationFactory<Program>` 建立記憶體中的 TestServer,搭配 `IClassFixture` 在測試類別間共享。 |
| 28 | |
| 29 | ### 基本流程 |
| 30 | |
| 31 | 1. 測試類別實作 `IClassFixture<WebApplicationFactory<Program>>` |
| 32 | 2. 透過建構函式注入 Factory |
| 33 | 3. 使用 `factory.CreateClient()` 建立 HttpClient |
| 34 | 4. 對 API 端點發送請求並驗證回應 |
| 35 | |
| 36 | ### 自訂 Factory |
| 37 | |
| 38 | 繼承 `WebApplicationFactory<Program>` 並覆寫 `ConfigureWebHost`: |
| 39 | |
| 40 | - 移除原本的 `DbContextOptions`,改用 `UseInMemoryDatabase` |
| 41 | - 使用 `services.Replace()` 替換外部服務為測試版本 |
| 42 | - 設定 `builder.UseEnvironment("Testing")` 使用測試環境 |
| 43 | |
| 44 | ### 測試基底類別 |
| 45 | |
| 46 | 建立 `IntegrationTestBase` 封裝共用邏輯: |
| 47 | |
| 48 | - Factory 與 HttpClient 初始化 |
| 49 | - `SeedShipperAsync()` 等資料準備方法 |
| 50 | - `CleanupDatabaseAsync()` 清理方法 |
| 51 | - 實作 `IDisposable` 確保資源釋放 |
| 52 | |
| 53 | > 完整程式碼範例(基本使用、自訂 Factory、測試基底類別)請參考 [references/webapplicationfactory-examples.md](references/webapplicationfactory-examples.md) |
| 54 | |
| 55 | ## 原則二:使用 AwesomeAssertions.Web 驗證 HTTP 回應 |
| 56 | |
| 57 | 提供流暢的 HTTP 狀態碼斷言與強型別回應驗證: |
| 58 | |
| 59 | - **狀態碼斷言**:`Be200Ok()`、`Be201Created()`、`Be400BadRequest()`、`Be404NotFound()` 等 |
| 60 | - **Satisfy<T> 驗證**:`response.Should().Be200Ok().And.Satisfy<T>(result => { ... })` |
| 61 | - **優勢**:取代手動 `ReadAsStringAsync()` + `JsonSerializer.Deserialize<T>()` 的冗長程式碼 |
| 62 | |
| 63 | ## 原則三:使用 System.Net.Http.Json 簡化 JSON 操作 |
| 64 | |
| 65 | - **`PostAsJsonAsync(url, object)`** — 取代手動 `JsonSerializer.Serialize` + `new StringContent` |
| 66 | - **`ReadFromJsonAsync<T>()`** — 取代手動 `ReadAsStringAsync` + `JsonSerializer.Deserialize` |
| 67 | |
| 68 | > 完整斷言與 JSON 操作範例請參考 [references/assertion-and-json-examples.md](references/assertion-and-json-examples.md) |
| 69 | |
| 70 | ## 三個層級的整合測試策略 |
| 71 | |
| 72 | | Level | 特色 | 測試重點 | Factory 設定 | |
| 73 | | ----- | ------------------------ | -------------------------------- | ------------------------- | |
| 74 | | 1 | 無資料庫、無 Service | API 輸入輸出、路由、狀態碼 | 直接使用原始 Factory | |
| 75 | | 2 | 有 Service(NSubstitute)| 依賴注入配置、服務互動 | ConfigureTestServices | |
| 76 | | 3 | 完整架構 + 資料庫 | 真實 CRUD、資料完整性 | InMemoryDatabase 替換 | |
| 77 | |
| 78 | ### Level 1:簡單的 WebApi 專案 |
| 79 | |
| 80 | 最簡單的形式,直接使用 `WebApplicationFactory<Program>` 測試各個 API 端點的輸入輸出。 |
| 81 | |
| 82 | ### Level 2:相依 Service 的 WebApi 專案 |
| 83 | |
| 84 | 使用 NSubstitute 建立 Service stub,透過自訂 Factory 的 `ConfigureTestServices` 注入測試用服務。 |
| 85 | |
| 86 | ### Level 3:完整的 WebApi 專案 |
| 87 | |
| 88 | 包含真實的資料庫操作,移除原本的 `DbContextOptions` 並改用 InMemoryDatabase,在測試中呼叫 `EnsureCreated()` 建立結構。 |
| 89 | |
| 90 | > 完整三個層級的程式碼範例請參考 [references/three-level-testing-strategy.md](references/three-level-testing-strategy.md) |
| 91 | |
| 92 | ## CRUD 操作測試 |
| 93 | |
| 94 | ### 測試涵蓋範圍 |
| 95 | |
| 96 | | 操作 | 測試重點 | |
| 97 | | ------ | ---------------------------------------------- | |
| 98 | | GET | 單一資源查詢、集合查詢、不存在資源回傳 404 | |
| 99 | | POST | 建立成功回傳 201、驗證錯誤回傳 400 | |
| 100 | | PUT | 更新成功、不存在資源處理 | |
| 101 | | DELETE | 刪除成功回傳 204、不存在資源回傳 404 | |
| 102 | |
| 103 | ### 測試資料管理 |
| 104 | |
| 105 | - 每個測試開始前呼叫 `CleanupDatabaseAsync()` 確保乾淨狀態 |
| 106 | - 使用 `SeedShipperAsync()` 等方法準備測試資料 |
| 107 | - 使用 `Satisfy<T>()` 驗證回應內容的正確性 |
| 108 | |
| 109 | 完整的 CRUD 操作測試程式碼請參考 **[references/crud-test-examples.md](references/crud-test-examples.md)** |
| 110 | |
| 111 | ## 專案結構建議 |
| 112 | |
| 113 | ```text |
| 114 | tests/ |
| 115 | ├── Sample.WebApplication.UnitTests/ # 單元測試 |
| 116 | ├── Sample.WebApplication.Integration.Tests/ # 整合測試 |
| 117 | │ ├── Controllers/ # 控制器整合測試 |
| 118 | │ ├── Infrastructure/ # 測試基礎設施 |
| 119 | │ │ └── CustomWebApplicationFactory.cs |
| 120 | │ ├── IntegrationTestBase.cs # 測試基底類別 |
| 121 | │ └── GlobalUsings.cs |
| 122 | └── Sample.WebApplication.E2ETests/ # 端對端測試 |
| 123 | ``` |
| 124 | |
| 125 | ## 常見錯誤排除 |
| 126 | |
| 127 | ### `'ObjectAssertions' 未包含 'Be200Ok' 的定義` |
| 128 | |
| 129 | 此錯誤通常是因為安裝了不相容的斷言套件組合。請確認基礎斷言庫與 Web 擴充套件版本一致。 |
| 130 | |
| 131 | ### `Program` 類別無法存取 |
| 132 | |
| 133 | 確保主專案的 `Program.cs` 包含以下宣告,讓測試專案可以存取: |
| 134 | |
| 135 | ```csharp |
| 136 | public partial class Program { } |
| 137 | ``` |
| 138 | |
| 139 | ### 測試資料互相干擾 |
| 140 | |
| 141 | - 每個測試開始前清理資料庫 |
| 142 | - 不要依賴其他測試的執行順序 |
| 143 | - 使用唯一識別碼避免資料衝突 |
| 144 | |
| 145 | ## 套件相容性 |
| 146 | |
| 147 | | 基礎斷言庫 | 正確的套件 | |
| 148 | | -------------------------- | |