diff --git a/TUnit.Engine.Tests/PropertyInjectionInitFailureTests.cs b/TUnit.Engine.Tests/PropertyInjectionInitFailureTests.cs
new file mode 100644
index 0000000000..d2e14a00f8
--- /dev/null
+++ b/TUnit.Engine.Tests/PropertyInjectionInitFailureTests.cs
@@ -0,0 +1,21 @@
+using Shouldly;
+using TUnit.Engine.Tests.Enums;
+
+namespace TUnit.Engine.Tests;
+
+public class PropertyInjectionInitFailureTests(TestMode testMode) : InvokableTestBase(testMode)
+{
+ [Test]
+ public async Task Test()
+ {
+ await RunTestsWithFilter(
+ "/*/*/PropertyInjectionInitFailureTests/*",
+ [
+ result => result.ResultSummary.Outcome.ShouldBe("Failed"),
+ result => result.ResultSummary.Counters.Total.ShouldBe(1),
+ result => result.ResultSummary.Counters.Passed.ShouldBe(0),
+ result => result.ResultSummary.Counters.Failed.ShouldBe(1),
+ result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
+ ]);
+ }
+}
diff --git a/TUnit.Engine/Building/Interfaces/ITestBuilder.cs b/TUnit.Engine/Building/Interfaces/ITestBuilder.cs
index 018c6c061e..24aeb2921b 100644
--- a/TUnit.Engine/Building/Interfaces/ITestBuilder.cs
+++ b/TUnit.Engine/Building/Interfaces/ITestBuilder.cs
@@ -16,7 +16,7 @@ internal interface ITestBuilder
///
/// Whether this test is reusing the discovery instance
/// An executable test ready for execution
- Task BuildTestAsync(TestMetadata metadata, TestBuilder.TestData testData, TestBuilderContext testBuilderContext, bool isReusingDiscoveryInstance = false);
+ Task BuildTestAsync(TestMetadata metadata, TestBuilder.TestData testData, TestBuilderContext testBuilderContext, bool isReusingDiscoveryInstance = false, CancellationToken cancellationToken = default);
///
/// Builds all executable tests from a single TestMetadata using its DataCombinationGenerator delegate.
@@ -28,7 +28,7 @@ internal interface ITestBuilder
#if NET6_0_OR_GREATER
[RequiresUnreferencedCode("Test building in reflection mode uses generic type resolution which requires unreferenced code")]
#endif
- Task> BuildTestsFromMetadataAsync(TestMetadata metadata, TestBuildingContext buildingContext);
+ Task> BuildTestsFromMetadataAsync(TestMetadata metadata, TestBuildingContext buildingContext, CancellationToken cancellationToken = default);
///
/// Streaming version that yields tests as they're built without buffering
diff --git a/TUnit.Engine/Building/TestBuilder.cs b/TUnit.Engine/Building/TestBuilder.cs
index 509d0757f2..7d2d0d22ea 100644
--- a/TUnit.Engine/Building/TestBuilder.cs
+++ b/TUnit.Engine/Building/TestBuilder.cs
@@ -117,7 +117,7 @@ private async Task
private async IAsyncEnumerable>> GetInitializedDataRowsAsync(
IDataSourceAttribute dataSource,
- DataGeneratorMetadata dataGeneratorMetadata)
+ DataGeneratorMetadata dataGeneratorMetadata,
+ [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken = default)
{
// Inject properties into data source during discovery (IAsyncInitializer deferred to execution)
var propertyInjectedDataSource = await _objectLifecycleService.InjectPropertiesAsync(
dataSource,
dataGeneratorMetadata.TestBuilderContext.Current.StateBag,
dataGeneratorMetadata.TestInformation,
- dataGeneratorMetadata.TestBuilderContext.Current.Events);
+ dataGeneratorMetadata.TestBuilderContext.Current.Events,
+ cancellationToken);
// Now get data rows from the property-injected data source
await foreach (var dataRow in propertyInjectedDataSource.GetDataRowsAsync(dataGeneratorMetadata))
@@ -854,7 +859,7 @@ private async Task GetDataSourcesAsync(IDataSourceAttrib
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Hook discovery service handles mode-specific logic; reflection calls suppressed in AOT mode")]
[UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Hook discovery service handles mode-specific logic; dynamic code suppressed in AOT mode")]
- public async Task BuildTestAsync(TestMetadata metadata, TestData testData, TestBuilderContext testBuilderContext, bool isReusingDiscoveryInstance = false)
+ public async Task BuildTestAsync(TestMetadata metadata, TestData testData, TestBuilderContext testBuilderContext, bool isReusingDiscoveryInstance = false, CancellationToken cancellationToken = default)
{
// Discover instance hooks for closed generic types (no-op in source gen mode)
if (metadata.TestClassType is { IsGenericType: true, IsGenericTypeDefinition: false })
@@ -864,7 +869,7 @@ public async Task BuildTestAsync(TestMetadata metadata,
var testId = TestIdentifierService.GenerateTestId(metadata, testData);
- var context = await CreateTestContextAsync(testId, metadata, testData, testBuilderContext);
+ var context = await CreateTestContextAsync(testId, metadata, testData, testBuilderContext, cancellationToken);
// Mark if this test is reusing the discovery instance (already initialized)
context.IsDiscoveryInstanceReused = isReusingDiscoveryInstance;
@@ -923,7 +928,7 @@ public async Task BuildTestAsync(TestMetadata metadata,
// in InvokePostResolutionEventsAsync after dependencies are resolved
try
{
- await RegisterTestArgumentsAsync(context);
+ await RegisterTestArgumentsAsync(context, cancellationToken);
}
catch (Exception ex)
{
@@ -1044,10 +1049,10 @@ private static void CollectAllDependencies(AbstractExecutableTest test, HashSet<
return firstSkipAttribute?.Reason;
}
- private async ValueTask CreateTestContextAsync(string testId, TestMetadata metadata, TestData testData, TestBuilderContext testBuilderContext)
+ private async ValueTask CreateTestContextAsync(string testId, TestMetadata metadata, TestData testData, TestBuilderContext testBuilderContext, CancellationToken cancellationToken = default)
{
// Use attributes from context if available, or create new ones
- var attributes = testBuilderContext.InitializedAttributes ?? await InitializeAttributesAsync(metadata.GetOrCreateAttributes());
+ var attributes = testBuilderContext.InitializedAttributes ?? await InitializeAttributesAsync(metadata.GetOrCreateAttributes(), cancellationToken);
if (testBuilderContext.DataSourceAttribute != null && testBuilderContext.DataSourceAttribute is not NoDataSource)
{
@@ -1089,7 +1094,7 @@ private async ValueTask CreateTestContextAsync(string testId, TestM
/// Registers test arguments for property injection and reference counting.
/// Called during test building, before dependencies are resolved.
///
- private async Task RegisterTestArgumentsAsync(TestContext context)
+ private async Task RegisterTestArgumentsAsync(TestContext context, CancellationToken cancellationToken = default)
{
var discoveredTest = new DiscoveredTest