$npx -y skills add kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-advanced-tunit-fundamentalsTUnit 新世代測試框架入門完整指南。當需要使用 TUnit 建立測試專案或從 xUnit 遷移至 TUnit 時使用。涵蓋 Source Generator 驅動測試發現、AOT 編譯支援、流暢式非同步斷言。包含專案建立、[Test] 屬性、生命週期管理、並行控制與 xUnit 語法對照。 Make sure to use this skill whenever the user mentions TUnit, Source Generator testing, AOT test framework, TUnit vs xUnit, or migra
| 1 | # TUnit 新世代測試框架入門基礎 |
| 2 | |
| 3 | ## TUnit 框架核心特色 |
| 4 | |
| 5 | ### 1. Source Generator 驅動的測試發現 |
| 6 | |
| 7 | TUnit 與傳統測試框架最大的差異在於使用 Source Generator 在**編譯時期**完成測試發現,避免反射成本、完全支援 Native AOT、更快的啟動時間。 |
| 8 | |
| 9 | ```csharp |
| 10 | // TUnit 在編譯時期就透過 Source Generator 產生測試註冊程式碼 |
| 11 | public class ModernTests |
| 12 | { |
| 13 | [Test] // 編譯時期就被處理和最佳化 |
| 14 | public async Task TestMethod() |
| 15 | { |
| 16 | await Assert.That(true).IsTrue(); |
| 17 | } |
| 18 | } |
| 19 | ``` |
| 20 | |
| 21 | ### 2. AOT (Ahead-of-Time) 編譯支援 |
| 22 | |
| 23 | ```text |
| 24 | 傳統 JIT:C# 原始碼 → IL 中間碼 → 執行時期 JIT 編譯 → 機器碼 → 執行 |
| 25 | AOT: C# 原始碼 → 編譯時期直接產生 → 機器碼 → 直接執行 |
| 26 | ``` |
| 27 | |
| 28 | AOT 優勢:超快啟動時間、更小的記憶體占用、可預測的效能、更適合容器化部署。大型專案可達 10-30 倍啟動時間改善。 |
| 29 | |
| 30 | ### 3. Microsoft.Testing.Platform 採用 |
| 31 | |
| 32 | TUnit 建構在微軟最新的 Microsoft.Testing.Platform 之上,而非傳統的 VSTest 平台。 |
| 33 | |
| 34 | **重要注意事項:** TUnit 專案**不需要**也**不應該**安裝 `Microsoft.NET.Test.Sdk` 套件。 |
| 35 | |
| 36 | ### 4. 預設並行執行 |
| 37 | |
| 38 | TUnit 將並行執行設為預設,需要時可用 `[NotInParallel("GroupName")]` 控制特定測試群組依序執行。 |
| 39 | |
| 40 | ## TUnit 專案建立 |
| 41 | |
| 42 | 支援手動建立(console 模板 + TUnit 套件)或使用 TUnit Template(`dotnet new tunit`,推薦)。專案需設定 csproj(TUnit 套件、IsTestProject)與 GlobalUsings.cs。所有測試方法**必須是非同步的**(`async Task`)。 |
| 43 | |
| 44 | > 完整專案建立步驟與 csproj 範例請參閱 [references/project-setup.md](references/project-setup.md) |
| 45 | |
| 46 | ## 測試屬性與參數化 |
| 47 | |
| 48 | ### 基本測試 [Test] |
| 49 | |
| 50 | TUnit 統一使用 `[Test]` 屬性,不像 xUnit 區分 `[Fact]` 和 `[Theory]`: |
| 51 | |
| 52 | ```csharp |
| 53 | [Test] |
| 54 | public async Task Add_輸入1和2_應回傳3() |
| 55 | { |
| 56 | var calculator = new Calculator(); |
| 57 | var result = calculator.Add(1, 2); |
| 58 | await Assert.That(result).IsEqualTo(3); |
| 59 | } |
| 60 | ``` |
| 61 | |
| 62 | ### 參數化測試 [Arguments] |
| 63 | |
| 64 | ```csharp |
| 65 | [Test] |
| 66 | [Arguments(1, 2, 3)] |
| 67 | [Arguments(-1, 1, 0)] |
| 68 | [Arguments(0, 0, 0)] |
| 69 | public async Task Add_多組輸入_應回傳正確結果(int a, int b, int expected) |
| 70 | { |
| 71 | var calculator = new Calculator(); |
| 72 | var result = calculator.Add(a, b); |
| 73 | await Assert.That(result).IsEqualTo(expected); |
| 74 | } |
| 75 | ``` |
| 76 | |
| 77 | ## TUnit.Assertions 斷言系統 |
| 78 | |
| 79 | TUnit 採用流暢式(Fluent)斷言設計,所有斷言都是非同步的。支援相等性、布林值、數值比較、字串、集合、例外等多種斷言,並可透過 `And` / `Or` 組合條件。 |
| 80 | |
| 81 | ```csharp |
| 82 | await Assert.That(actual).IsEqualTo(expected); |
| 83 | await Assert.That(email).Contains("@").And.EndsWith(".com"); |
| 84 | await Assert.That(() => action()).Throws<InvalidOperationException>(); |
| 85 | ``` |
| 86 | |
| 87 | > 完整斷言類型與範例請參閱 [references/tunit-assertions-detail.md](references/tunit-assertions-detail.md) |
| 88 | |
| 89 | ## 測試生命週期管理 |
| 90 | |
| 91 | TUnit 支援建構式 / `Dispose` 模式,以及 `[Before(Test)]`、`[Before(Class)]`、`[After(Test)]`、`[After(Class)]` 等屬性。 |
| 92 | |
| 93 | ```text |
| 94 | 執行順序:Before(Class) → 建構式 → Before(Test) → 測試方法 → After(Test) → Dispose → After(Class) |
| 95 | ``` |
| 96 | |
| 97 | > 完整生命週期範例與屬性對照表請參閱 [references/lifecycle-management.md](references/lifecycle-management.md) |
| 98 | |
| 99 | ## 並行執行控制與 xUnit 遷移 |
| 100 | |
| 101 | TUnit 預設並行執行所有測試,使用 `[NotInParallel]` 控制特定群組依序執行。提供完整的 xUnit → TUnit 語法對照表、遷移範例、CLI 執行指令與 IDE 整合設定。 |
| 102 | |
| 103 | > 完整並行控制、語法對照與遷移範例請參閱 [references/xunit-migration.md](references/xunit-migration.md) |
| 104 | |
| 105 | ## 效能比較 |
| 106 | |
| 107 | | 場景 | xUnit | TUnit | TUnit AOT | 效能提升 | |
| 108 | | ---------------- | ------- | ------- | --------- | --------- | |
| 109 | | **簡單測試執行** | 1,400ms | 1,000ms | 60ms | 23x (AOT) | |
| 110 | | **非同步測試** | 1,400ms | 930ms | 26ms | 54x (AOT) | |
| 111 | | **並行測試** | 1,425ms | 999ms | 54ms | 26x (AOT) | |
| 112 | |
| 113 | ## 常見問題與解決方案 |
| 114 | |
| 115 | 1. **套件相容性** — 移除 `Microsoft.NET.Test.Sdk`,TUnit 使用新的測試平台 |
| 116 | 2. **IDE 整合問題** — 確認 IDE 版本支援 Microsoft.Testing.Platform,啟用相關預覽功能 |
| 117 | 3. **非同步斷言遺忘** — 所有斷言都需要 `await`,測試方法必須是 `async Task` |
| 118 | |
| 119 | ## 適用場景評估 |
| 120 | |
| 121 | ### 適合使用 TUnit |
| 122 | |
| 123 | 1. **全新專案**:沒有歷史包袱 |
| 124 | 2. **效能要求高**:大型測試套件(1000+ 測試) |
| 125 | 3. **技術棧先進**:使用 .NET 8+,計劃採用 AOT |
| 126 | 4. **CI/CD 重度使用**:測試執行時間直接影響部署頻率 |
| 127 | 5. **容器化部署**:快速啟動時間很重要 |
| 128 | |
| 129 | ### 暫時不建議 |
| 130 | |
| 131 | 1. **Legacy 專案**:已有大量 xUnit 測試 |
| 132 | 2. **保守團隊**:需要穩定性勝過創新性 |
| 133 | 3. **複雜測試生態**:大量使用 xUnit 特定套件 |
| 134 | 4. **舊版 .NET**:還在 .NET 6/7 |
| 135 | |
| 136 | ## 輸出格式 |
| 137 | |
| 138 | - 產生 TUnit 測試類別(含 [Test]、[Arguments] 屬性) |
| 139 | - 產生 .csproj 設定(TUnit 套件參考) |
| 140 | - 提供 GlobalUsings.cs 設定 |
| 141 | - 包含 xUnit 到 TUnit 的遷移程式碼對照 |
| 142 | |
| 143 | ## 參考資源 |
| 144 | |
| 145 | ### 原始文章 |
| 146 | |
| 147 | 本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章: |
| 148 | |
| 149 | - **Day 28 - TUnit 入門 - 下世代 .NET 測試框架探索** |
| 150 | - 鐵人賽文章:https://ithelp.ithome.com.tw/articles/10377828 |
| 151 | - 範例程式碼:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day28 |
| 152 | |
| 153 | ### 官方資源 |
| 154 | |
| 155 | - [TUnit 官方網站](https://tunit.dev/) |
| 156 | - [TUnit GitHub](https://github.com/thomhurst/TUnit) |
| 157 | - [從 xUnit 遷移指南](https://tunit.dev/docs/migration/xunit) |
| 158 | |
| 159 | ### Microsoft 官方文件 |
| 160 | |
| 161 | - [Microsoft.Testing.Platform 介紹](https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-intro) |
| 162 | - [原生 AOT 部署](https://learn.microsoft.com/zh-tw/dotnet/core/deploying/native-aot) |
| 163 | |
| 164 | ### 相關技能 |
| 165 | |
| 166 | - `dotnet-testing-advanced-tunit-advanced` - TUnit 進階功能 |
| 167 | - `dotnet-testing-advanced-xunit-upgrade-guide` - xUnit 升級指南 |
| 168 | - `dotnet-testing-xunit-project-setup` - xUnit 專案設定(比較用) |