diff --git a/TUnit.Core/AbstractDynamicTest.cs b/TUnit.Core/AbstractDynamicTest.cs
index e0d3464b6c..6097c24001 100644
--- a/TUnit.Core/AbstractDynamicTest.cs
+++ b/TUnit.Core/AbstractDynamicTest.cs
@@ -101,6 +101,11 @@ public class DynamicTest<[DynamicallyAccessedMembers(
///
public int? CreatorLineNumber { get; set; }
+ ///
+ /// Custom display name for this dynamic test. If not set, a default name will be generated.
+ ///
+ public string? DisplayName { get; set; }
+
public override IEnumerable GetTests()
{
var result = new DynamicDiscoveryResult
@@ -112,7 +117,8 @@ public override IEnumerable GetTests()
TestClassType = typeof(T),
CreatorFilePath = CreatorFilePath,
CreatorLineNumber = CreatorLineNumber,
- DynamicTestIndex = DynamicTestIndex
+ DynamicTestIndex = DynamicTestIndex,
+ DisplayName = DisplayName
};
yield return result;
diff --git a/TUnit.Core/DynamicTestMetadata.cs b/TUnit.Core/DynamicTestMetadata.cs
new file mode 100644
index 0000000000..eb3d4e77f3
--- /dev/null
+++ b/TUnit.Core/DynamicTestMetadata.cs
@@ -0,0 +1,125 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace TUnit.Core;
+
+///
+/// Unified metadata class for dynamic tests.
+/// Used by both AOT/source-generated mode and reflection mode for tests created via
+/// DynamicTestBuilderAttribute or runtime test variant creation.
+///
+public sealed class DynamicTestMetadata : TestMetadata, IDynamicTestMetadata
+{
+ private readonly DynamicDiscoveryResult _dynamicResult;
+
+ public DynamicTestMetadata(DynamicDiscoveryResult dynamicResult)
+ {
+ _dynamicResult = dynamicResult;
+ }
+
+ public int DynamicTestIndex => _dynamicResult.DynamicTestIndex;
+
+ public string? DisplayName => _dynamicResult.DisplayName;
+
+ ///
+ /// Parent test ID for test variants created at runtime.
+ ///
+ public string? ParentTestId => _dynamicResult.ParentTestId;
+
+ ///
+ /// Relationship to parent test for test variants.
+ ///
+ public Enums.TestRelationship? Relationship => _dynamicResult.Relationship;
+
+ ///
+ /// Custom properties for test variants.
+ ///
+ public Dictionary? Properties => _dynamicResult.Properties;
+
+ [field: AllowNull, MaybeNull]
+ public override Func CreateExecutableTestFactory
+ {
+ get => field ??= CreateExecutableTest;
+ }
+
+ private AbstractExecutableTest CreateExecutableTest(ExecutableTestCreationContext context, TestMetadata metadata)
+ {
+ var modifiedContext = new ExecutableTestCreationContext
+ {
+ TestId = context.TestId,
+ DisplayName = _dynamicResult.DisplayName ?? context.DisplayName,
+ Arguments = _dynamicResult.TestMethodArguments ?? context.Arguments,
+ ClassArguments = _dynamicResult.TestClassArguments ?? context.ClassArguments,
+ Context = context.Context,
+ TestClassInstanceFactory = context.TestClassInstanceFactory
+ };
+
+ // Apply runtime test variant properties
+ if (_dynamicResult.ParentTestId != null)
+ {
+ modifiedContext.Context.ParentTestId = _dynamicResult.ParentTestId;
+ }
+
+ if (_dynamicResult.Relationship.HasValue)
+ {
+ modifiedContext.Context.Relationship = _dynamicResult.Relationship.Value;
+ }
+
+ if (_dynamicResult.Properties != null)
+ {
+ foreach (var kvp in _dynamicResult.Properties)
+ {
+ modifiedContext.Context.StateBag.Items[kvp.Key] = kvp.Value;
+ }
+ }
+
+ // Create instance factory
+ var createInstance = async (TestContext testContext) =>
+ {
+ // If we have a factory from discovery, use it
+ if (modifiedContext.TestClassInstanceFactory != null)
+ {
+ return await modifiedContext.TestClassInstanceFactory();
+ }
+
+ // Check if there's a ClassConstructor to use
+ if (testContext.ClassConstructor != null)
+ {
+ var testBuilderContext = TestBuilderContext.FromTestContext(testContext, null);
+ var classConstructorMetadata = new ClassConstructorMetadata
+ {
+ TestSessionId = metadata.TestSessionId,
+ TestBuilderContext = testBuilderContext
+ };
+
+ return await testContext.ClassConstructor.Create(metadata.TestClassType, classConstructorMetadata);
+ }
+
+ // Fall back to default instance factory
+ var instance = metadata.InstanceFactory(Type.EmptyTypes, modifiedContext.ClassArguments);
+
+ // Handle property injections
+ foreach (var propertyInjection in metadata.PropertyInjections)
+ {
+ var value = propertyInjection.ValueFactory();
+ propertyInjection.Setter(instance, value);
+ }
+
+ return instance;
+ };
+
+ var invokeTest = metadata.TestInvoker ?? throw new InvalidOperationException("Test invoker is null");
+
+ return new ExecutableTest(createInstance,
+ async (instance, args, ctx, ct) =>
+ {
+ await invokeTest(instance, args);
+ })
+ {
+ TestId = modifiedContext.TestId,
+ Metadata = metadata,
+ Arguments = modifiedContext.Arguments,
+ ClassArguments = modifiedContext.ClassArguments,
+ Context = modifiedContext.Context
+ };
+ }
+}
diff --git a/TUnit.Core/IDynamicTestMetadata.cs b/TUnit.Core/IDynamicTestMetadata.cs
index 23b71dc093..4b4f2223ad 100644
--- a/TUnit.Core/IDynamicTestMetadata.cs
+++ b/TUnit.Core/IDynamicTestMetadata.cs
@@ -10,4 +10,9 @@ public interface IDynamicTestMetadata
/// Used to generate unique test IDs when multiple dynamic tests target the same method.
///
int DynamicTestIndex { get; }
+
+ ///
+ /// Custom display name for this dynamic test. If null, a default name will be generated.
+ ///
+ string? DisplayName { get; }
}
\ No newline at end of file
diff --git a/TUnit.Engine/Building/Collectors/AotTestDataCollector.cs b/TUnit.Engine/Building/Collectors/AotTestDataCollector.cs
index 18a314bd48..b673db2071 100644
--- a/TUnit.Engine/Building/Collectors/AotTestDataCollector.cs
+++ b/TUnit.Engine/Building/Collectors/AotTestDataCollector.cs
@@ -131,7 +131,7 @@ private Task CreateMetadataFromDynamicDiscoveryResult(DynamicDisco
var testName = methodInfo.Name;
- return Task.FromResult(new AotDynamicTestMetadata(result)
+ return Task.FromResult(new DynamicTestMetadata(result)
{
TestName = testName,
TestClassType = result.TestClassType,
@@ -296,74 +296,6 @@ private static MethodMetadata CreateDummyMethodMetadata(Type type, string method
};
}
- private sealed class AotDynamicTestMetadata(DynamicDiscoveryResult dynamicResult) : TestMetadata, IDynamicTestMetadata
- {
- public int DynamicTestIndex => dynamicResult.DynamicTestIndex;
-
- public override Func CreateExecutableTestFactory
- {
- get => (context, metadata) =>
- {
- // For dynamic tests, we need to use the specific arguments from the dynamic result
- var modifiedContext = new ExecutableTestCreationContext
- {
- TestId = context.TestId,
- DisplayName = context.DisplayName,
- Arguments = dynamicResult.TestMethodArguments ?? context.Arguments,
- ClassArguments = dynamicResult.TestClassArguments ?? context.ClassArguments,
- Context = context.Context
- };
-
- // Create instance and test invoker for the dynamic test
- var createInstance = async (TestContext testContext) =>
- {
- object instance;
-
- // Check if there's a ClassConstructor to use
- if (testContext.ClassConstructor != null)
- {
- var testBuilderContext = TestBuilderContext.FromTestContext(testContext, null);
- var classConstructorMetadata = new ClassConstructorMetadata
- {
- TestSessionId = "", // Dynamic tests don't have session IDs
- TestBuilderContext = testBuilderContext
- };
-
- instance = await testContext.ClassConstructor.Create(metadata.TestClassType, classConstructorMetadata);
- }
- else
- {
- instance = metadata.InstanceFactory(Type.EmptyTypes, modifiedContext.ClassArguments);
- }
-
- // Handle property injections
- foreach (var propertyInjection in metadata.PropertyInjections)
- {
- var value = propertyInjection.ValueFactory();
- propertyInjection.Setter(instance, value);
- }
-
- return instance;
- };
-
- var invokeTest = metadata.TestInvoker ?? throw new InvalidOperationException("Test invoker is null");
-
- return new ExecutableTest(createInstance,
- async (instance, args, context, ct) =>
- {
- await invokeTest(instance, args);
- })
- {
- TestId = modifiedContext.TestId,
- Metadata = metadata,
- Arguments = modifiedContext.Arguments,
- ClassArguments = modifiedContext.ClassArguments,
- Context = modifiedContext.Context
- };
- };
- }
- }
-
private sealed class FailedDynamicTestMetadata(Exception exception) : TestMetadata
{
public override Func CreateExecutableTestFactory
diff --git a/TUnit.Engine/Building/TestBuilderPipeline.cs b/TUnit.Engine/Building/TestBuilderPipeline.cs
index 2e33d9baab..96da81767f 100644
--- a/TUnit.Engine/Building/TestBuilderPipeline.cs
+++ b/TUnit.Engine/Building/TestBuilderPipeline.cs
@@ -294,9 +294,11 @@ private async IAsyncEnumerable BuildTestsFromSingleMetad
};
var testId = TestIdentifierService.GenerateTestId(resolvedMetadata, testData);
+ var dynamicMetadata = (IDynamicTestMetadata)resolvedMetadata;
+ var baseDisplayName = dynamicMetadata.DisplayName ?? resolvedMetadata.TestName;
var displayName = repeatCount > 0
- ? $"{resolvedMetadata.TestName} (Repeat {repeatIndex + 1}/{repeatCount + 1})"
- : resolvedMetadata.TestName;
+ ? $"{baseDisplayName} (Repeat {repeatIndex + 1}/{repeatCount + 1})"
+ : baseDisplayName;
// Create TestDetails for dynamic tests
var testDetails = new TestDetails
@@ -326,6 +328,12 @@ private async IAsyncEnumerable BuildTestsFromSingleMetad
// Set the TestDetails on the context
context.Metadata.TestDetails = testDetails;
+ // Set custom display name for dynamic tests if specified
+ if (dynamicMetadata.DisplayName != null)
+ {
+ context.Metadata.DisplayName = dynamicMetadata.DisplayName;
+ }
+
// Invoke discovery event receivers to properly handle all attribute behaviors
await InvokeDiscoveryEventReceiversAsync(context).ConfigureAwait(false);
diff --git a/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs b/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
index e087d0229e..caac547fb9 100644
--- a/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
+++ b/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
@@ -1922,7 +1922,7 @@ private Task CreateMetadataFromDynamicDiscoveryResult(DynamicDisco
var testName = GenerateTestName(result.TestClassType, methodInfo);
- var metadata = new DynamicReflectionTestMetadata(result.TestClassType, methodInfo, result)
+ var metadata = new DynamicTestMetadata(result)
{
TestName = testName,
TestClassType = result.TestClassType,
@@ -2091,85 +2091,4 @@ private static TestMetadata CreateFailedTestMetadataForDynamicTest(DynamicDiscov
};
}
- private sealed class DynamicReflectionTestMetadata : TestMetadata, IDynamicTestMetadata
- {
- private readonly DynamicDiscoveryResult _dynamicResult;
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
- private readonly Type _testClass;
- private readonly MethodInfo _testMethod;
-
- public DynamicReflectionTestMetadata(
- [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type testClass,
- MethodInfo testMethod,
- DynamicDiscoveryResult dynamicResult)
- {
- _testClass = testClass;
- _testMethod = testMethod;
- _dynamicResult = dynamicResult;
- }
-
- public int DynamicTestIndex => _dynamicResult.DynamicTestIndex;
-
- public override Func CreateExecutableTestFactory
- {
- get => (context, metadata) =>
- {
- // For dynamic tests, we need to use the specific arguments from the dynamic result
- var modifiedContext = new ExecutableTestCreationContext
- {
- TestId = context.TestId,
- DisplayName = context.DisplayName,
- Arguments = _dynamicResult.TestMethodArguments ?? context.Arguments,
- ClassArguments = _dynamicResult.TestClassArguments ?? context.ClassArguments,
- Context = context.Context
- };
-
- // Create a regular ExecutableTest with the modified context
- // Create instance and test invoker for the dynamic test
- Func> createInstance = async (TestContext testContext) =>
- {
- // Try to create instance with ClassConstructor attribute
- var attributes = metadata.AttributeFactory();
- var classConstructorInstance = await ClassConstructorHelper.TryCreateInstanceWithClassConstructor(
- attributes,
- _testClass,
- metadata.TestSessionId,
- testContext).ConfigureAwait(false);
-
- if (classConstructorInstance != null)
- {
- return classConstructorInstance;
- }
-
- // Fall back to default instance factory
- var instance = metadata.InstanceFactory(Type.EmptyTypes, modifiedContext.ClassArguments);
-
- // Handle property injections
- foreach (var propertyInjection in metadata.PropertyInjections)
- {
- var value = propertyInjection.ValueFactory();
- propertyInjection.Setter(instance, value);
- }
-
- return instance;
- };
-
- var invokeTest = metadata.TestInvoker ?? throw new InvalidOperationException("Test invoker is null");
-
- return new ExecutableTest(createInstance,
- async (instance, args, context, ct) =>
- {
- await invokeTest(instance, args).ConfigureAwait(false);
- })
- {
- TestId = modifiedContext.TestId,
- Metadata = metadata,
- Arguments = modifiedContext.Arguments,
- ClassArguments = modifiedContext.ClassArguments,
- Context = modifiedContext.Context
- };
- };
- }
- }
-
}
diff --git a/TUnit.Engine/Services/TestRegistry.cs b/TUnit.Engine/Services/TestRegistry.cs
index 3b4013f0fa..cc76b93a1d 100644
--- a/TUnit.Engine/Services/TestRegistry.cs
+++ b/TUnit.Engine/Services/TestRegistry.cs
@@ -256,7 +256,7 @@ private async Task CreateMetadataFromDynamicDiscoveryResult(Dynami
var testName = methodInfo.Name;
- return await Task.FromResult(new RuntimeDynamicTestMetadata(result.TestClassType, methodInfo, result)
+ return await Task.FromResult(new DynamicTestMetadata(result)
{
TestName = testName,
TestClassType = result.TestClassType,
@@ -340,83 +340,6 @@ private sealed class PendingDynamicTest
}
- private sealed class RuntimeDynamicTestMetadata : TestMetadata, IDynamicTestMetadata
- {
- private readonly DynamicDiscoveryResult _dynamicResult;
- private readonly Type _testClass;
- private readonly MethodInfo _testMethod;
-
- public RuntimeDynamicTestMetadata(Type testClass, MethodInfo testMethod, DynamicDiscoveryResult dynamicResult)
- {
- _testClass = testClass;
- _testMethod = testMethod;
- _dynamicResult = dynamicResult;
- }
-
- public int DynamicTestIndex => _dynamicResult.DynamicTestIndex;
-
- public override Func CreateExecutableTestFactory
- {
- get => (context, metadata) =>
- {
- var modifiedContext = new ExecutableTestCreationContext
- {
- TestId = context.TestId,
- DisplayName = _dynamicResult.DisplayName ?? context.DisplayName,
- Arguments = _dynamicResult.TestMethodArguments ?? context.Arguments,
- ClassArguments = _dynamicResult.TestClassArguments ?? context.ClassArguments,
- Context = context.Context
- };
-
- if (_dynamicResult.ParentTestId != null)
- {
- modifiedContext.Context.ParentTestId = _dynamicResult.ParentTestId;
- }
-
- if (_dynamicResult.Relationship.HasValue)
- {
- modifiedContext.Context.Relationship = _dynamicResult.Relationship.Value;
- }
-
- if (_dynamicResult.Properties != null)
- {
- foreach (var kvp in _dynamicResult.Properties)
- {
- modifiedContext.Context.StateBag.Items[kvp.Key] = kvp.Value;
- }
- }
-
- var createInstance = (TestContext testContext) =>
- {
- var instance = metadata.InstanceFactory(Type.EmptyTypes, modifiedContext.ClassArguments);
-
- foreach (var propertyInjection in metadata.PropertyInjections)
- {
- var value = propertyInjection.ValueFactory();
- propertyInjection.Setter(instance, value);
- }
-
- return Task.FromResult(instance);
- };
-
- var invokeTest = metadata.TestInvoker ?? throw new InvalidOperationException("Test invoker is null");
-
- return new ExecutableTest(createInstance,
- async (instance, args, context, ct) =>
- {
- await invokeTest(instance, args);
- })
- {
- TestId = modifiedContext.TestId,
- Metadata = metadata,
- Arguments = modifiedContext.Arguments,
- ClassArguments = modifiedContext.ClassArguments,
- Context = modifiedContext.Context
- };
- };
- }
- }
-
///
/// Optimized method to get dependencies without LINQ allocations
///
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
index afda7f52fe..1e249359c3 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet10_0.verified.txt
@@ -646,6 +646,16 @@ namespace
{
public static T Argument() { }
}
+ public sealed class DynamicTestMetadata : .TestMetadata, .IDynamicTestMetadata
+ {
+ public DynamicTestMetadata(.DynamicDiscoveryResult dynamicResult) { }
+ public override <.ExecutableTestCreationContext, .TestMetadata, .AbstractExecutableTest> CreateExecutableTestFactory { get; }
+ public string? DisplayName { get; }
+ public int DynamicTestIndex { get; }
+ public string? ParentTestId { get; }
+ public .? Properties { get; }
+ public .? Relationship { get; }
+ }
public class DynamicTest<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..NonPublicConstructors | ..PublicMethods | ..NonPublicMethods | ..PublicFields | ..NonPublicFields | ..PublicProperties)] T> : .AbstractDynamicTest, .IDynamicTestCreatorLocation
where T : class
{
@@ -653,6 +663,7 @@ namespace
public .<> Attributes { get; set; }
public string? CreatorFilePath { get; set; }
public int? CreatorLineNumber { get; set; }
+ public string? DisplayName { get; set; }
public object?[]? TestClassArguments { get; set; }
public .<>? TestMethod { get; set; }
public object?[]? TestMethodArguments { get; set; }
@@ -857,6 +868,7 @@ namespace
}
public interface IDynamicTestMetadata
{
+ string? DisplayName { get; }
int DynamicTestIndex { get; }
}
public interface IDynamicTestSource
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt
index 5c77ae9d16..39b85adbb0 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet8_0.verified.txt
@@ -646,6 +646,16 @@ namespace
{
public static T Argument() { }
}
+ public sealed class DynamicTestMetadata : .TestMetadata, .IDynamicTestMetadata
+ {
+ public DynamicTestMetadata(.DynamicDiscoveryResult dynamicResult) { }
+ public override <.ExecutableTestCreationContext, .TestMetadata, .AbstractExecutableTest> CreateExecutableTestFactory { get; }
+ public string? DisplayName { get; }
+ public int DynamicTestIndex { get; }
+ public string? ParentTestId { get; }
+ public .? Properties { get; }
+ public .? Relationship { get; }
+ }
public class DynamicTest<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..NonPublicConstructors | ..PublicMethods | ..NonPublicMethods | ..PublicFields | ..NonPublicFields | ..PublicProperties)] T> : .AbstractDynamicTest, .IDynamicTestCreatorLocation
where T : class
{
@@ -653,6 +663,7 @@ namespace
public .<> Attributes { get; set; }
public string? CreatorFilePath { get; set; }
public int? CreatorLineNumber { get; set; }
+ public string? DisplayName { get; set; }
public object?[]? TestClassArguments { get; set; }
public .<>? TestMethod { get; set; }
public object?[]? TestMethodArguments { get; set; }
@@ -857,6 +868,7 @@ namespace
}
public interface IDynamicTestMetadata
{
+ string? DisplayName { get; }
int DynamicTestIndex { get; }
}
public interface IDynamicTestSource
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt
index 7d0b7502f7..c4adff816e 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.DotNet9_0.verified.txt
@@ -646,6 +646,16 @@ namespace
{
public static T Argument() { }
}
+ public sealed class DynamicTestMetadata : .TestMetadata, .IDynamicTestMetadata
+ {
+ public DynamicTestMetadata(.DynamicDiscoveryResult dynamicResult) { }
+ public override <.ExecutableTestCreationContext, .TestMetadata, .AbstractExecutableTest> CreateExecutableTestFactory { get; }
+ public string? DisplayName { get; }
+ public int DynamicTestIndex { get; }
+ public string? ParentTestId { get; }
+ public .? Properties { get; }
+ public .? Relationship { get; }
+ }
public class DynamicTest<[.(..None | ..PublicParameterlessConstructor | ..PublicConstructors | ..NonPublicConstructors | ..PublicMethods | ..NonPublicMethods | ..PublicFields | ..NonPublicFields | ..PublicProperties)] T> : .AbstractDynamicTest, .IDynamicTestCreatorLocation
where T : class
{
@@ -653,6 +663,7 @@ namespace
public .<> Attributes { get; set; }
public string? CreatorFilePath { get; set; }
public int? CreatorLineNumber { get; set; }
+ public string? DisplayName { get; set; }
public object?[]? TestClassArguments { get; set; }
public .<>? TestMethod { get; set; }
public object?[]? TestMethodArguments { get; set; }
@@ -857,6 +868,7 @@ namespace
}
public interface IDynamicTestMetadata
{
+ string? DisplayName { get; }
int DynamicTestIndex { get; }
}
public interface IDynamicTestSource
diff --git a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt
index 232141cff3..c5f7481604 100644
--- a/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt
+++ b/TUnit.PublicAPI/Tests.Core_Library_Has_No_API_Changes.Net4_7.verified.txt
@@ -623,6 +623,16 @@ namespace
{
public static T Argument() { }
}
+ public sealed class DynamicTestMetadata : .TestMetadata, .IDynamicTestMetadata
+ {
+ public DynamicTestMetadata(.DynamicDiscoveryResult dynamicResult) { }
+ public override <.ExecutableTestCreationContext, .TestMetadata, .AbstractExecutableTest> CreateExecutableTestFactory { get; }
+ public string? DisplayName { get; }
+ public int DynamicTestIndex { get; }
+ public string? ParentTestId { get; }
+ public .? Properties { get; }
+ public .? Relationship { get; }
+ }
public class DynamicTest : .AbstractDynamicTest, .IDynamicTestCreatorLocation
where T : class
{
@@ -630,6 +640,7 @@ namespace
public .<> Attributes { get; set; }
public string? CreatorFilePath { get; set; }
public int? CreatorLineNumber { get; set; }
+ public string? DisplayName { get; set; }
public object?[]? TestClassArguments { get; set; }
public .<>? TestMethod { get; set; }
public object?[]? TestMethodArguments { get; set; }
@@ -834,6 +845,7 @@ namespace
}
public interface IDynamicTestMetadata
{
+ string? DisplayName { get; }
int DynamicTestIndex { get; }
}
public interface IDynamicTestSource
diff --git a/TUnit.TestProject/DynamicTests/DynamicTestDisplayNameTests.cs b/TUnit.TestProject/DynamicTests/DynamicTestDisplayNameTests.cs
new file mode 100644
index 0000000000..627e4f1fe8
--- /dev/null
+++ b/TUnit.TestProject/DynamicTests/DynamicTestDisplayNameTests.cs
@@ -0,0 +1,62 @@
+using TUnit.TestProject.Attributes;
+
+namespace TUnit.TestProject.DynamicTests;
+
+///
+/// Tests that validate custom DisplayName is correctly applied to dynamic tests.
+///
+[EngineTest(ExpectedResult.Pass)]
+public class DynamicTestDisplayNameTests
+{
+ public async Task TestWithCustomDisplayName()
+ {
+ await Assert.That(TestContext.Current!.Metadata.DisplayName).IsEqualTo("My Custom Dynamic Test Name");
+ }
+
+ public async Task TestWithParameterizedDisplayName(int value)
+ {
+ await Assert.That(TestContext.Current!.Metadata.DisplayName).IsEqualTo($"Dynamic Test with value {value}");
+ }
+
+ public async Task TestWithDefaultDisplayName()
+ {
+ // When DisplayName is not set, it should fall back to the default generated name
+ await Assert.That(TestContext.Current!.Metadata.DisplayName).Contains("TestWithDefaultDisplayName");
+ }
+
+#pragma warning disable TUnitWIP0001
+ [DynamicTestBuilder]
+#pragma warning restore TUnitWIP0001
+ public void BuildTests(DynamicTestBuilderContext context)
+ {
+ // Test with a custom display name
+ context.AddTest(new DynamicTest
+ {
+ TestMethod = c => c.TestWithCustomDisplayName(),
+ DisplayName = "My Custom Dynamic Test Name"
+ });
+
+ // Test with parameterized display name
+ context.AddTest(new DynamicTest
+ {
+ TestMethod = c => c.TestWithParameterizedDisplayName(42),
+ TestMethodArguments = [42],
+ DisplayName = "Dynamic Test with value 42"
+ });
+
+ // Another parameterized test with different value
+ context.AddTest(new DynamicTest
+ {
+ TestMethod = c => c.TestWithParameterizedDisplayName(100),
+ TestMethodArguments = [100],
+ DisplayName = "Dynamic Test with value 100"
+ });
+
+ // Test without custom display name (should use default)
+ context.AddTest(new DynamicTest
+ {
+ TestMethod = c => c.TestWithDefaultDisplayName()
+ // DisplayName intentionally not set
+ });
+ }
+}