Advanced Techniques for Data-Driven Testing
TUnit provides three advanced data sources: MethodDataSource, ClassDataSource, and Matrix Tests. MethodDataSource is the most flexible, supporting dynamic generation and loading from external files; ClassDataSource is suitable for sharing data across test classes and integrating with AutoFixture; Matrix Tests automatically generates all parameter combinations (be careful to control the number to avoid explosive growth).
For complete examples and a comparison table, see references/data-driven-testing.md
Properties Attribute Marking and Test Filtering
Basic Properties Usage
[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");
}
Establish a Consistent Property Naming Convention
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 Test Filter Execution
TUnit uses dotnet run instead of 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]"
Filter syntax notes:
- Path pattern
/*/*/*/*represents the Assembly/Namespace/Class/Method levels - Property names are case-sensitive
- Combined conditions must be correctly enclosed in parentheses
Test Lifecycle Management
TUnit provides a complete set of lifecycle hooks:[Before(Class)] → constructor → [Before(Test)] → test method → [After(Test)] → Dispose → [After(Class)]. There are also Assembly/TestSession-level and [BeforeEvery]/[AfterEvery] global hooks. The constructor always runs first, and BeforeClass/AfterClass each run only once.
For the complete attribute family and examples, see references/lifecycle-management.md
Dependency Injection Patterns
Core Concepts of TUnit Dependency Injection
TUnit's dependency injection is built on 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();
}
}
Using TUnit Dependency Injection
[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");
}
}
Comparison of TUnit DI vs Manual Dependency Creation
| Aspect | TUnit DI | Manual Dependency Creation |
|---|---|---|
| Setup Complexity | Set up once, reuse repeatedly | Must be created manually for each test |
| Maintainability | Dependency changes require modifying only one place | All tests that use it need to be modified |
| Consistency | Consistent with the DI in production code | May be inconsistent with the actual application |
| Test Readability | Focuses on test logic | Interfered with by dependency creation code |
| Scope Management | Automatically manages service scopes | Requires manual management of object lifecycles |
| Risk of Errors | The framework guarantees correct dependency injection | Some dependencies may be missed or created incorrectly |
Execution Control and Test Quality
[Retry(n)]: Use only for unstable tests caused by external dependencies (network, file locking), not for logic errors[Timeout(ms)]: Set a reasonable upper limit for performance-sensitive tests, paired withStopwatchto verify the SLA[DisplayName]: Supports{0}parameter interpolation, making test reports closer to business language
For complete examples (Retry/Timeout/DisplayName), see references/execution-control.md
ASP.NET Core Integration Testing
Using in TUnit WebApplicationFactory<Program> Perform ASP.NET Core integration testing by implementing IDisposable to manage the lifecycle. Covers API response validation, Content-Type header checks, as well as performance benchmarks and concurrent load testing.
For a complete WebApplicationFactory integration and load testing example, see references/aspnet-integration.md
TUnit + Testcontainers Infrastructure Orchestration
Use [Before(Assembly)] / [After(Assembly)] to manage multi-container orchestration such as PostgreSQL, Redis, Kafka, etc. at the Assembly level, together with NetworkBuilder to establish a shared network. Containers are started only once, greatly reducing startup time and resource consumption while maintaining data isolation between tests.
For a complete multi-container orchestration and global sharing example, see references/tunit-testcontainers.md
TUnit Engine Modes
Source Generation Mode (Default Mode)
████████╗██╗ ██╗███╗ ██╗██╗████████╗
╚══██╔══╝██║ ██║████╗ ██║██║╚══██╔══╝
██║ ██║ ██║██╔██╗ ██║██║ ██║
██║ ██║ ██║██║╚██╗██║██║ ██║
██║ ╚██████╔╝██║ ╚████║██║ ██║
╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝
Engine Mode: SourceGenerated
Features and Advantages:
- Compile-time generation: All test discovery logic is generated at compile time, no runtime reflection required
- Excellent performance: Several times faster than reflection mode
- Type safety: Compile-time validation of test configuration and data sources
- AOT compatible: Full support for Native AOT compilation
Reflection Mode
# 啟用反射模式
dotnet run -- --reflection
# 或設定環境變數
$env:TUNIT_EXECUTION_MODE = "reflection"
dotnet run
Applicable scenarios:
- Dynamic test discovery
- F# and VB.NET projects (used automatically)
- Certain test patterns that rely on reflection
Native AOT Support
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
dotnet publish -c Release
Common Issues and Troubleshooting
Abnormal test statistics issue
Symptoms: 測試摘要: 總計: 0, 失敗: 0, 成功: 0
Resolution steps:
- Ensure the project file is configured correctly:
<PropertyGroup>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
- Ensure GlobalUsings.cs is correct:
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;
- Special configuration for integration tests:
// 在 WebApi 專案的 Program.cs 最後加上
public partial class Program { } // 讓整合測試可以存取
- Clean and rebuild:
dotnet clean; dotnet build
dotnet test --verbosity normal
Source Generator Related Issues
Issue: Test classes cannot be discovered
- Solution: Ensure the project is fully rebuilt (
dotnet clean; dotnet build)
Issue: Strange errors during compilation
- Solution: Check whether there are other Source Generator packages, and consider updating to compatible versions
Diagnostic Options
# .editorconfig
tunit.enable_verbose_diagnostics = true
<PropertyGroup>
<TUnitEnableVerboseDiagnostics>true</TUnitEnableVerboseDiagnostics>
</PropertyGroup>
Practical Recommendations
Selection Strategy for Data-Driven Testing
- MethodDataSource: Suitable for dynamic data, complex objects, and external file loading
- ClassDataSource: Suitable for shared data, AutoFixture integration, and reuse across test classes
- Matrix Tests: Suitable for combinatorial testing, but be mindful of the number of parameters to avoid explosive growth
Execution Control Best Practices
- Retry: Use only for genuinely unstable external dependency tests
- Timeout: Set reasonable limits for performance-sensitive tests
- DisplayName: Make test reports more aligned with business language
Integration Testing Strategy
- Use WebApplicationFactory for complete Web API testing
- Use TUnit + Testcontainers to build complex multi-service test environments
- Manage complex dependencies through the property injection system
- Only test functionality that actually exists, and avoid testing endpoints that do not exist
Template Files
| File Name | Description |
|---|---|
| data-source-examples.cs | MethodDataSource, ClassDataSource examples |
| matrix-tests-examples.cs | Matrix Tests combinatorial testing examples |
| lifecycle-di-examples.cs | Lifecycle management and dependency injection examples |
| execution-control-examples.cs | Retry, Timeout, DisplayName examples |
| aspnet-integration-tests.cs | ASP.NET Core Integration Testing Example |
| testcontainers-examples.cs | Testcontainers Infrastructure Orchestration Example |
Output Format
- Generate data-driven test classes (.cs files) using MethodDataSource/ClassDataSource/Matrix Tests
- Includes MicrosoftDependencyInjectionDataSource dependency injection configuration
- Includes Retry/Timeout/DisplayName execution control examples
- Generate WebApplicationFactory integration tests and Testcontainers multi-container orchestration code
Reference Resources
Original Articles
This skill content is distilled from the "Old-School Software Engineer's Testing Training - 30-Day Challenge" series of articles:
-
Day 29 - TUnit Advanced Applications: Data-Driven Testing and Dependency Injection In-Depth Practical Combat
- Ironman Series Article:https://ithelp.ithome.com.tw/articles/10377970
- Sample Code:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day29
-
Day 30 - TUnit Advanced Applications - Execution Control and Test Quality and ASP.NET Core Integration Testing Practical Combat
- Ironman Series Article:https://ithelp.ithome.com.tw/articles/10378176
- Sample Code:https://github.com/kevintsengtw/30Days_in_Testing_Samples/tree/main/day30
TUnit Official Resources
Advanced Feature Documentation
- TUnit Method Data Source Documentation
- TUnit Class Data Source Documentation
- TUnit Matrix Tests Documentation
- TUnit Properties Documentation
- TUnit Dependency Injection Documentation
- TUnit Retrying Documentation
- TUnit Timeouts Documentation
- TUnit Engine Modes Documentation
- TUnit ASP.NET Core Documentation
- TUnit Complex Test Infrastructure
Testcontainers Related Resources
Related Skills
dotnet-testing-advanced-tunit-fundamentals- TUnit Fundamentals (Prerequisite Skill)dotnet-testing-advanced-aspnet-integration-testing- ASP.NET Core Integration Testingdotnet-testing-advanced-testcontainers-database- Testcontainers Database Testing