Complete guide to advanced TUnit usage. Use when you need to perform data-driven tests, dependency injection, or integration tests with TUnit. Covers MethodDataSource, ClassDataSource, Matrix Tests, and Properties filtering. Includes Retry/Timeout controls, WebApplicationFactory integration, and Testcontainers multi-service orchestration. Keywords: TUnit advanced, MethodDataSource, ClassDataSource, Matrix Tests, MatrixDataSource, MicrosoftDependencyInjectionDataSource, Property, Retry, Timeout, data-driven testing, test filtering, WebApplicationFactory TUnit, multi-container orchestration
TUnit 提供 MethodDataSource、ClassDataSource、Matrix Tests 三種進階資料來源。MethodDataSource 最靈活,支援動態產生與外部檔案載入;ClassDataSource 適合跨測試類別共享資料與 AutoFixture 整合;Matrix Tests 自動產生所有參數組合(注意控制數量避免爆炸性增長)。
完整範例與比較表請參閱 references/data-driven-testing.md
[Test]
[Property("Category", "Database")]
[Property("Priority", "High")]
public async Task DatabaseTest_高優先級_應能透過屬性過濾()
{
await Assert.That(true).IsTrue();
}
[Test]
[Property("Category", "Unit")]
[Property("Priority", "Medium")]
public async Task UnitTest_中等優先級_基本驗證()
{
await Assert.That(1 + 1).IsEqualTo(2);
}
[Test]
[Property("Category", "Integration")]
[Property("Priority", "Low")]
[Property("Environment", "Development")]
public async Task IntegrationTest_低優先級_僅開發環境執行()
{
await Assert.That("Hello World").Contains("World");
}
public static class TestProperties
{
// 測試類別
public const string CATEGORY_UNIT = "Unit";
public const string CATEGORY_INTEGRATION = "Integration";
public const string CATEGORY_E2E = "E2E";
// 優先級
public const string PRIORITY_CRITICAL = "Critical";
public const string PRIORITY_HIGH = "High";
public const string PRIORITY_MEDIUM = "Medium";
public const string PRIORITY_LOW = "Low";
// 環境
public const string ENV_DEVELOPMENT = "Development";
public const string ENV_STAGING = "Staging";
public const string ENV_PRODUCTION = "Production";
}
[Test]
[Property("Category", TestProperties.CATEGORY_UNIT)]
[Property("Priority", TestProperties.PRIORITY_HIGH)]
public async Task ExampleTest_使用常數_確保一致性()
{
await Assert.That(1 + 1).IsEqualTo(2);
}
TUnit 使用 dotnet run 而不是 dotnet test:
# 只執行單元測試
dotnet run --treenode-filter "/*/*/*/*[Category=Unit]"
# 只執行高優先級測試
dotnet run --treenode-filter "/*/*/*/*[Priority=High]"
# 組合條件:執行高優先級的單元測試
dotnet run --treenode-filter "/*/*/*/*[(Category=Unit)&(Priority=High)]"
# 或條件:執行單元測試或冒煙測試
dotnet run --treenode-filter "/*/*/*/*[(Category=Unit)|(Suite=Smoke)]"
# 執行特定功能的測試
dotnet run --treenode-filter "/*/*/*/*[Feature=OrderProcessing]"
過濾語法注意事項:
/*/*/*/* 代表 Assembly/Namespace/Class/Method 層級TUnit 提供完整的生命週期鉤子:[Before(Class)] → 建構式 → [Before(Test)] → 測試方法 → [After(Test)] → Dispose → [After(Class)]。另有 Assembly/TestSession 層級與 [BeforeEvery]/[AfterEvery] 全域鉤子。建構式永遠最先執行,BeforeClass/AfterClass 各只執行一次。
完整屬性家族與範例請參閱 references/lifecycle-management.md
TUnit 的依賴注入建構在 Data Source Generators 基礎上:
public class MicrosoftDependencyInjectionDataSourceAttribute : DependencyInjectionDataSourceAttribute<IServiceScope>
{
private static readonly IServiceProvider ServiceProvider = CreateSharedServiceProvider();
public override IServiceScope CreateScope(DataGeneratorMetadata dataGeneratorMetadata)
{
return ServiceProvider.CreateScope();
}
public override object? Create(IServiceScope scope, Type type)
{
return scope.ServiceProvider.GetService(type);
}
private static IServiceProvider CreateSharedServiceProvider()
{
return new ServiceCollection()
.AddSingleton<IOrderRepository, MockOrderRepository>()
.AddSingleton<IDiscountCalculator, MockDiscountCalculator>()
.AddSingleton<IShippingCalculator, MockShippingCalculator>()
.AddSingleton<ILogger<OrderService>, MockLogger<OrderService>>()
.AddTransient<OrderService>()
.BuildServiceProvider();
}
}
[MicrosoftDependencyInjectionDataSource]
public class DependencyInjectionTests(OrderService orderService)
{
[Test]
public async Task CreateOrder_使用TUnit依賴注入_應正確運作()
{
// Arrange - 依賴已經透過 TUnit DI 自動注入
var items = new List<OrderItem>
{
new() { ProductId = "PROD001", ProductName = "測試商品", UnitPrice = 100m, Quantity = 2 }
};
// Act
var order = await orderService.CreateOrderAsync("CUST001", CustomerLevel.VIP會員, items);
// Assert
await Assert.That(order).IsNotNull();
await Assert.That(order.CustomerId).IsEqualTo("CUST001");
await Assert.That(order.CustomerLevel).IsEqualTo(CustomerLevel.VIP會員);
}
[Test]
public async Task TUnitDependencyInjection_驗證自動注入_服務應為正確類型()
{
await Assert.That(orderService).IsNotNull();
await Assert.That(orderService.GetType().Name).IsEqualTo("OrderService");
}
}
| 特性 | TUnit DI | 手動依賴建立 | | :------------- | :----------------------- | :------------------------- | | 設定複雜度 | 一次設定,重複使用 | 每個測試都需要手動建立 | | 可維護性 | 依賴變更只需修改一個地方 | 需要修改所有使用的測試 | | 一致性 | 與產品程式碼的 DI 一致 | 可能與實際應用程式不一致 | | 測試可讀性 | 專注於測試邏輯 | 被依賴建立程式碼干擾 | | 範圍管理 | 自動管理服務範圍 | 需要手動管理物件生命週期 | | 錯誤風險 | 框架保證依賴正確注入 | 可能遺漏或錯誤建立某些依賴 |
[Retry(n)]:僅用於外部依賴造成的不穩定測試(網路、檔案鎖定),不用於邏輯錯誤[Timeout(ms)]:為效能敏感測試設定合理上限,搭配 Stopwatch 驗證 SLA[DisplayName]:支援 {0} 參數插值,讓測試報告更貼近業務語言完整範例(Retry/Timeout/DisplayName)請參閱 references/execution-control.md
在 TUnit 中使用 WebApplicationFactory<Program> 進行 ASP.NET Core 整合測試,透過實作 IDisposable 管理生命週期。涵蓋 API 回應驗證、Content-Type 標頭檢查,以及效能基準與並行負載測試。
完整 WebApplicationFactory 整合與負載測試範例請參閱 references/aspnet-integration.md
使用 [Before(Assembly)] / [After(Assembly)] 在 Assembly 層級管理 PostgreSQL、Redis、Kafka 等多容器編排,搭配 NetworkBuilder 建立共用網路。容器僅啟動一次,大幅減少啟動時間與資源消耗,同時保持測試間的資料隔離。
完整多容器編排與全域共享範例請參閱 references/tunit-testcontainers.md
████████╗██╗ ██╗███╗ ██╗██╗████████╗
╚══██╔══╝██║ ██║████╗ ██║██║╚══██╔══╝
██║ ██║ ██║██╔██╗ ██║██║ ██║
██║ ██║ ██║██║╚██╗██║██║ ██║
██║ ╚██████╔╝██║ ╚████║██║ ██║
╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝
Engine Mode: SourceGenerated
特色與優勢:
# 啟用反射模式
dotnet run -- --reflection
# 或設定環境變數
$env:TUNIT_EXECUTION_MODE = "reflection"
dotnet run
適用場景:
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
dotnet publish -c Release
問題現象: 測試摘要: 總計: 0, 失敗: 0, 成功: 0
解決步驟:
<PropertyGroup>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading.Tasks;
global using TUnit.Core;
global using TUnit.Assertions;
global using TUnit.Assertions.Extensions;
// 在 WebApi 專案的 Program.cs 最後加上
public partial class Program { } // 讓整合測試可以存取
dotnet clean; dotnet build
dotnet test --verbosity normal
問題:測試類別無法被發現
dotnet clean; dotnet build)問題:編譯時出現奇怪錯誤
# .editorconfig
tunit.enable_verbose_diagnostics = true
<PropertyGroup>
<TUnitEnableVerboseDiagnostics>true</TUnitEnableVerboseDiagnostics>
</PropertyGroup>
| 檔案名稱 | 說明 | | ------------------------------------------------------------------------ | -------------------------------------- | | data-source-examples.cs | MethodDataSource、ClassDataSource 範例 | | matrix-tests-examples.cs | Matrix Tests 組合測試範例 | | lifecycle-di-examples.cs | 生命週期管理與依賴注入範例 | | execution-control-examples.cs | Retry、Timeout、DisplayName 範例 | | aspnet-integration-tests.cs | ASP.NET Core 整合測試範例 | | testcontainers-examples.cs | Testcontainers 基礎設施編排範例 |
本技能內容提煉自「老派軟體工程師的測試修練 - 30 天挑戰」系列文章:
Day 29 - TUnit 進階應用:資料驅動測試與依賴注入深度實戰
Day 30 - TUnit 進階應用 - 執行控制與測試品質和 ASP.NET Core 整合測試實戰
dotnet-testing-advanced-tunit-fundamentals - TUnit 基礎(前置技能)dotnet-testing-advanced-aspnet-integration-testing - ASP.NET Core 整合測試dotnet-testing-advanced-testcontainers-database - Testcontainers 資料庫測試npx skills add kevintsengtw/dotnet-测试-高级-tunit-高级下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer