diff --git a/src/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs b/src/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
index 8f888742792..8b264099829 100644
--- a/src/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
+++ b/src/TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
@@ -2321,13 +2321,22 @@ private static TestMetadata CreateFailedMethodGenericMetadata(
FilePath = ExtractFilePath(method) ?? "Unknown",
LineNumber = ExtractLineNumber(method) ?? 0,
MethodMetadata = ReflectionMetadataBuilder.CreateMethodMetadata(type, method),
- AttributeFactory = () => method.GetCustomAttributes().ToArray(),
+ AttributeFactory = () => GetFailedTestAttributes(method),
DataSources = [],
ClassDataSources = [],
PropertyDataSources = []
};
}
+ ///
+ /// Attributes for a test whose discovery failed. Such a test has no producing data row, so the
+ /// row-scoped data sources (such as ) are dropped, the same way
+ /// TestBuilder.ScopeRowAttributesToTest drops the rows that did not produce a test.
+ /// Otherwise every row's Skip, DisplayName and Categories would be applied to the failed test (#7076).
+ ///
+ private static Attribute[] GetFailedTestAttributes(MethodInfo method)
+ => TestBuilder.ScopeRowAttributesToTest(method.GetCustomAttributes().ToArray(), null, null);
+
private static string? ExtractFilePath(MethodInfo method)
{
return method.GetCustomAttribute()?.File;
@@ -2387,8 +2396,7 @@ private static TestMetadata CreateFailedTestMetadata(
FilePath = ExtractFilePath(method) ?? "Unknown",
LineNumber = ExtractLineNumber(method) ?? 0,
MethodMetadata = ReflectionMetadataBuilder.CreateMethodMetadata(type, method),
- AttributeFactory = () => method.GetCustomAttributes()
- .ToArray(),
+ AttributeFactory = () => GetFailedTestAttributes(method),
DataSources =
[
],
@@ -3350,7 +3358,7 @@ private static TestMetadata CreateFailedTestMetadataForDynamicBuilder(
FilePath = ExtractFilePath(method) ?? "Unknown",
LineNumber = ExtractLineNumber(method) ?? 0,
MethodMetadata = ReflectionMetadataBuilder.CreateMethodMetadata(type, method),
- AttributeFactory = () => method.GetCustomAttributes().ToArray(),
+ AttributeFactory = () => GetFailedTestAttributes(method),
DataSources = [],
ClassDataSources = [],
PropertyDataSources = []
diff --git a/tests/TUnit.Engine.Tests/Issue7076Tests.cs b/tests/TUnit.Engine.Tests/Issue7076Tests.cs
new file mode 100644
index 00000000000..b9af5b386fe
--- /dev/null
+++ b/tests/TUnit.Engine.Tests/Issue7076Tests.cs
@@ -0,0 +1,114 @@
+using System.Reflection;
+using Shouldly;
+using TUnit.Core;
+using TUnit.Core.Interfaces;
+using TUnit.Engine.Discovery;
+using TUnit.Engine.Tests.Enums;
+
+namespace TUnit.Engine.Tests;
+
+public class Issue7076Tests(TestMode testMode) : InvokableTestBase(testMode)
+{
+ [Test]
+ public async Task Arguments_Skip_Only_Skips_Its_Own_Row()
+ {
+ await RunTestsWithFilter(
+ "/*/*/SkippableArgumentTests/Test1",
+ [
+ result => result.ResultSummary.Outcome.ShouldBe("Completed"),
+ result => result.ResultSummary.Counters.Total.ShouldBe(3),
+ result => result.ResultSummary.Counters.Passed.ShouldBe(2),
+ result => result.ResultSummary.Counters.Failed.ShouldBe(0),
+ result => result.ResultSummary.Counters.NotExecuted.ShouldBe(1)
+ ]);
+ }
+
+ [Test]
+ public async Task Arguments_DisplayName_Only_Applies_To_Its_Own_Row()
+ {
+ await RunTestsWithFilter(
+ "/*/*/ArgumentsRowSkipTests/DisplayNameOnlyAppliesToItsOwnRow",
+ [
+ result => result.ResultSummary.Outcome.ShouldBe("Completed"),
+ result => result.ResultSummary.Counters.Total.ShouldBe(2),
+ result => result.ResultSummary.Counters.Passed.ShouldBe(2),
+ result => result.ResultSummary.Counters.Failed.ShouldBe(0),
+ result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
+ ]);
+ }
+
+ [Test]
+ public async Task ClassArguments_Skip_Only_Skips_Its_Own_Row()
+ {
+ await RunTestsWithFilter(
+ "/*/*/ClassArgumentsRowSkipTests/OnlyMarkedClassRowIsSkipped",
+ [
+ result => result.ResultSummary.Outcome.ShouldBe("Completed"),
+ result => result.ResultSummary.Counters.Total.ShouldBe(2),
+ result => result.ResultSummary.Counters.Passed.ShouldBe(1),
+ result => result.ResultSummary.Counters.Failed.ShouldBe(0),
+ result => result.ResultSummary.Counters.NotExecuted.ShouldBe(1)
+ ]);
+ }
+
+ [Test]
+ public async Task InheritedArguments_Skip_Only_Skips_Its_Own_Row()
+ {
+ await RunTestsWithFilter(
+ "/*/*/InheritedArgumentsRowSkipTests/OnlyMarkedInheritedRowIsSkipped",
+ [
+ result => result.ResultSummary.Outcome.ShouldBe("Completed"),
+ result => result.ResultSummary.Counters.Total.ShouldBe(2),
+ result => result.ResultSummary.Counters.Passed.ShouldBe(1),
+ result => result.ResultSummary.Counters.Failed.ShouldBe(0),
+ result => result.ResultSummary.Counters.NotExecuted.ShouldBe(1)
+ ]);
+ }
+
+ [Test]
+ public async Task ClassConstructor_Uses_Method_DataSource()
+ {
+ await RunTestsWithFilter(
+ "/*/*/ClassConstructorDataSourceTest/MethodDataSourceIsPassedToClassConstructor",
+ [
+ result => result.ResultSummary.Outcome.ShouldBe("Completed"),
+ result => result.ResultSummary.Counters.Total.ShouldBe(1),
+ result => result.ResultSummary.Counters.Passed.ShouldBe(1),
+ result => result.ResultSummary.Counters.Failed.ShouldBe(0),
+ result => result.ResultSummary.Counters.NotExecuted.ShouldBe(0)
+ ]);
+ }
+
+ [Test]
+ public Task Failed_Metadata_Filters_Method_DataSources()
+ {
+ var method = typeof(FailedMetadataDataSourceTest).GetMethod(nameof(FailedMetadataDataSourceTest.Test))!;
+ var collectorType = typeof(ReflectionTestDataCollector);
+ var exception = new InvalidOperationException("discovery failed");
+ var factories = new (string Name, object?[] Arguments)[]
+ {
+ ("CreateFailedTestMetadata", [typeof(FailedMetadataDataSourceTest), method, exception]),
+ ("CreateFailedMethodGenericMetadata", [typeof(FailedMetadataDataSourceTest), method, "generic resolution failed"]),
+ ("CreateFailedTestMetadataForDynamicBuilder", [typeof(FailedMetadataDataSourceTest), method, exception])
+ };
+
+ foreach (var (name, arguments) in factories)
+ {
+ var factory = collectorType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Static);
+ factory.ShouldNotBeNull(name);
+ var metadata = (TestMetadata)factory.Invoke(null, arguments)!;
+ metadata.AttributeFactory().OfType().ShouldBeEmpty(name);
+ }
+
+ return Task.CompletedTask;
+ }
+
+ private sealed class FailedMetadataDataSourceTest
+ {
+ [Arguments(1)]
+ [Arguments(2, Skip = "wip")]
+ public static void Test(int value)
+ {
+ }
+ }
+}
diff --git a/tests/TUnit.TestProject/Bugs/_7076/ArgumentsRowSkipTests.cs b/tests/TUnit.TestProject/Bugs/_7076/ArgumentsRowSkipTests.cs
new file mode 100644
index 00000000000..81feb04af4b
--- /dev/null
+++ b/tests/TUnit.TestProject/Bugs/_7076/ArgumentsRowSkipTests.cs
@@ -0,0 +1,81 @@
+using TUnit.Core.Interfaces;
+using TUnit.TestProject.Attributes;
+
+namespace TUnit.TestProject.Bugs._7076;
+
+[EngineTest(ExpectedResult.Pass)]
+public class ArgumentsRowSkipTests
+{
+ [Test]
+ [Arguments(1, DisplayName = "custom name for 1")]
+ [Arguments(2)]
+ public async Task DisplayNameOnlyAppliesToItsOwnRow(int value)
+ {
+ var displayName = TestContext.Current!.Metadata.DisplayName;
+
+ if (value == 1)
+ {
+ await Assert.That(displayName).IsEqualTo("custom name for 1");
+ }
+ else
+ {
+ await Assert.That(displayName).IsEqualTo("DisplayNameOnlyAppliesToItsOwnRow(2)");
+ }
+ }
+}
+
+[EngineTest(ExpectedResult.Pass)]
+[Arguments("a")]
+[Arguments("b", Skip = "skip only b")]
+public class ClassArgumentsRowSkipTests(string value)
+{
+ [Test]
+ public async Task OnlyMarkedClassRowIsSkipped()
+ {
+ await Assert.That(value).IsEqualTo("a");
+ }
+}
+
+[EngineTest(ExpectedResult.Pass)]
+[InheritsTests]
+public class InheritedArgumentsRowSkipTests : InheritedArgumentsRowSkipBase;
+
+public abstract class InheritedArgumentsRowSkipBase
+{
+ [Test]
+ [Arguments(1)]
+ [Arguments(2, Skip = "skip only 2")]
+ public async Task OnlyMarkedInheritedRowIsSkipped(int value)
+ {
+ await Assert.That(value).IsEqualTo(1);
+ }
+}
+
+[EngineTest(ExpectedResult.Pass)]
+[ClassConstructor]
+[ClassDataSource]
+public class ClassConstructorDataSourceTest(ClassConstructorFixture fixture)
+{
+ [Test]
+ [Arguments(42)]
+ public async Task MethodDataSourceIsPassedToClassConstructor(int value)
+ {
+ await Assert.That(value).IsEqualTo(42);
+ }
+}
+
+public sealed class ClassConstructorDataSourceProbe : IClassConstructor
+{
+ public Task