$npx -y skills add kevintsengtw/dotnet-testing-agent-skills --skill dotnet-testing-advanced-tunit-advancedTUnit 進階應用完整指南。當需要使用 TUnit 進行資料驅動測試、依賴注入或整合測試時使用。涵蓋 MethodDataSource、ClassDataSource、Matrix Tests、Properties 過濾。包含 Retry/Timeout 控制、WebApplicationFactory 整合、Testcontainers 多服務編排。 Make sure to use this skill whenever the user mentions TUnit advanced, MethodDataSource, ClassDataSou
| 1 | # TUnit 進階應用:資料驅動測試、依賴注入與整合測試實戰 |
| 2 | |
| 3 | ## 資料驅動測試進階技巧 |
| 4 | |
| 5 | TUnit 提供 MethodDataSource、ClassDataSource、Matrix Tests 三種進階資料來源。MethodDataSource 最靈活,支援動態產生與外部檔案載入;ClassDataSource 適合跨測試類別共享資料與 AutoFixture 整合;Matrix Tests 自動產生所有參數組合(注意控制數量避免爆炸性增長)。 |
| 6 | |
| 7 | > 完整範例與比較表請參閱 [references/data-driven-testing.md](references/data-driven-testing.md) |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Properties 屬性標記與測試過濾 |
| 12 | |
| 13 | ### 基本 Properties 使用 |
| 14 | |
| 15 | ```csharp |
| 16 | [Test] |
| 17 | [Property("Category", "Database")] |
| 18 | [Property("Priority", "High")] |
| 19 | public async Task DatabaseTest_高優先級_應能透過屬性過濾() |
| 20 | { |
| 21 | await Assert.That(true).IsTrue(); |
| 22 | } |
| 23 | |
| 24 | [Test] |
| 25 | [Property("Category", "Unit")] |
| 26 | [Property("Priority", "Medium")] |
| 27 | public async Task UnitTest_中等優先級_基本驗證() |
| 28 | { |
| 29 | await Assert.That(1 + 1).IsEqualTo(2); |
| 30 | } |
| 31 | |
| 32 | [Test] |
| 33 | [Property("Category", "Integration")] |
| 34 | [Property("Priority", "Low")] |
| 35 | [Property("Environment", "Development")] |
| 36 | public async Task IntegrationTest_低優先級_僅開發環境執行() |
| 37 | { |
| 38 | await Assert.That("Hello World").Contains("World"); |
| 39 | } |
| 40 | ``` |
| 41 | |
| 42 | ### 建立一致的屬性命名規範 |
| 43 | |
| 44 | ```csharp |
| 45 | public static class TestProperties |
| 46 | { |
| 47 | // 測試類別 |
| 48 | public const string CATEGORY_UNIT = "Unit"; |
| 49 | public const string CATEGORY_INTEGRATION = "Integration"; |
| 50 | public const string CATEGORY_E2E = "E2E"; |
| 51 | |
| 52 | // 優先級 |
| 53 | public const string PRIORITY_CRITICAL = "Critical"; |
| 54 | public const string PRIORITY_HIGH = "High"; |
| 55 | public const string PRIORITY_MEDIUM = "Medium"; |
| 56 | public const string PRIORITY_LOW = "Low"; |
| 57 | |
| 58 | // 環境 |
| 59 | public const string ENV_DEVELOPMENT = "Development"; |
| 60 | public const string ENV_STAGING = "Staging"; |
| 61 | public const string ENV_PRODUCTION = "Production"; |
| 62 | } |
| 63 | |
| 64 | [Test] |
| 65 | [Property("Category", TestProperties.CATEGORY_UNIT)] |
| 66 | [Property("Priority", TestProperties.PRIORITY_HIGH)] |
| 67 | public async Task ExampleTest_使用常數_確保一致性() |
| 68 | { |
| 69 | await Assert.That(1 + 1).IsEqualTo(2); |
| 70 | } |
| 71 | ``` |
| 72 | |
| 73 | ### TUnit 測試過濾執行 |
| 74 | |
| 75 | TUnit 使用 `dotnet run` 而不是 `dotnet test`: |
| 76 | |
| 77 | ```bash |
| 78 | # 只執行單元測試 |
| 79 | dotnet run --treenode-filter "/*/*/*/*[Category=Unit]" |
| 80 | |
| 81 | # 只執行高優先級測試 |
| 82 | dotnet run --treenode-filter "/*/*/*/*[Priority=High]" |
| 83 | |
| 84 | # 組合條件:執行高優先級的單元測試 |
| 85 | dotnet run --treenode-filter "/*/*/*/*[(Category=Unit)&(Priority=High)]" |
| 86 | |
| 87 | # 或條件:執行單元測試或冒煙測試 |
| 88 | dotnet run --treenode-filter "/*/*/*/*[(Category=Unit)|(Suite=Smoke)]" |
| 89 | |
| 90 | # 執行特定功能的測試 |
| 91 | dotnet run --treenode-filter "/*/*/*/*[Feature=OrderProcessing]" |
| 92 | ``` |
| 93 | |
| 94 | **過濾語法注意事項:** |
| 95 | - 路徑模式 `/*/*/*/*` 代表 Assembly/Namespace/Class/Method 層級 |
| 96 | - 屬性名稱大小寫敏感 |
| 97 | - 組合條件必須用括號正確包圍 |
| 98 | |
| 99 | --- |
| 100 | |
| 101 | ## 測試生命週期管理 |
| 102 | |
| 103 | TUnit 提供完整的生命週期鉤子:`[Before(Class)]` → 建構式 → `[Before(Test)]` → 測試方法 → `[After(Test)]` → Dispose → `[After(Class)]`。另有 Assembly/TestSession 層級與 `[BeforeEvery]`/`[AfterEvery]` 全域鉤子。建構式永遠最先執行,BeforeClass/AfterClass 各只執行一次。 |
| 104 | |
| 105 | > 完整屬性家族與範例請參閱 [references/lifecycle-management.md](references/lifecycle-management.md) |
| 106 | |
| 107 | --- |
| 108 | |
| 109 | ## 依賴注入模式 |
| 110 | |
| 111 | ### TUnit 依賴注入核心概念 |
| 112 | |
| 113 | TUnit 的依賴注入建構在 Data Source Generators 基礎上: |
| 114 | |
| 115 | ```csharp |
| 116 | public class MicrosoftDependencyInjectionDataSourceAttribute : DependencyInjectionDataSourceAttribute<IServiceScope> |
| 117 | { |
| 118 | private static readonly IServiceProvider ServiceProvider = CreateSharedServiceProvider(); |
| 119 | |
| 120 | public override IServiceScope CreateScope(DataGeneratorMetadata dataGeneratorMetadata) |
| 121 | { |
| 122 | return ServiceProvider.CreateScope(); |
| 123 | } |
| 124 | |
| 125 | public override object? Create(IServiceScope scope, Type type) |
| 126 | { |
| 127 | return scope.ServiceProvider.GetService(type); |
| 128 | } |
| 129 | |
| 130 | private static IServiceProvider CreateSharedServiceProvider() |
| 131 | { |
| 132 | return new ServiceCollection() |
| 133 | .AddSingleton<IOrderRepository, MockOrderRepository>() |
| 134 | .AddSingleton<IDiscountCalculator, MockDiscountCalculator>() |
| 135 | .AddSingleton<IShippingCalculator, MockShippingCalculator>() |
| 136 | .AddSingleton<ILogger<OrderService>, MockLogger<OrderService>>() |
| 137 | .AddTransient<OrderService>() |
| 138 | .BuildServiceProvider(); |
| 139 | } |
| 140 | } |
| 141 | ``` |
| 142 | |
| 143 | ### 使用 TUnit 依賴注入 |
| 144 | |
| 145 | ```csharp |
| 146 | [MicrosoftDependencyInjectionDataSource] |
| 147 | public class DependencyInjectionTests(OrderService orderService) |
| 148 | { |
| 149 | [Test] |
| 150 | public async Task CreateOrder_使用TUnit依賴注入_應正確運作() |
| 151 | { |
| 152 | // Arrange - 依賴已經透過 TUnit DI 自動注入 |
| 153 | var items = new List<OrderItem> |
| 154 | { |
| 155 | new() { ProductId = "PROD001", ProductName = "測試商品", UnitPrice = 100m, Quantity = 2 } |
| 156 | }; |
| 157 | |
| 158 | // Act |
| 159 | var order = await orderService.CreateOrderAsync("CUST001", CustomerLevel.VIP會員, items); |