From d176ec62c0a8969e0c311a64d07300b14e260d5c Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 12 Apr 2026 18:09:13 +1000 Subject: [PATCH] Expose test arguments as filterable properties Parameterized tests can now be filtered by their resolved argument values via `--treenode-filter`, e.g.: --treenode-filter "/*/*/MyTests/*[arg0=*foo*]" TestBuilder.InvokeDiscoveryEventReceiversAsync stringifies each entry in TestDetails.TestMethodArguments and adds it to the DiscoveredTestContext as arg0, arg1, ... so it flows into the existing property bag used by the filter matcher. Wildcards in the value are supported by the platform's built-in matcher. --- .../FilterByArgumentValueFilteringTests.cs | 21 +++++++++++++++++++ TUnit.Engine/Building/TestBuilder.cs | 10 +++++++++ .../FilterByArgumentValueTests.cs | 12 +++++++++++ 3 files changed, 43 insertions(+) create mode 100644 TUnit.Engine.Tests/FilterByArgumentValueFilteringTests.cs create mode 100644 TUnit.TestProject/FilterByArgumentValueTests.cs diff --git a/TUnit.Engine.Tests/FilterByArgumentValueFilteringTests.cs b/TUnit.Engine.Tests/FilterByArgumentValueFilteringTests.cs new file mode 100644 index 0000000000..8d72d7a81b --- /dev/null +++ b/TUnit.Engine.Tests/FilterByArgumentValueFilteringTests.cs @@ -0,0 +1,21 @@ +using Shouldly; +using TUnit.Engine.Tests.Enums; + +namespace TUnit.Engine.Tests; + +public class FilterByArgumentValueFilteringTests(TestMode testMode) : InvokableTestBase(testMode) +{ + [Test] + public async Task ExactMatch() + { + await RunTestsWithFilter( + "/*/*/FilterByArgumentValueTests/*[arg0=beta]", + [ + 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) + ]); + } +} diff --git a/TUnit.Engine/Building/TestBuilder.cs b/TUnit.Engine/Building/TestBuilder.cs index 207d8a24e6..579ff28fc0 100644 --- a/TUnit.Engine/Building/TestBuilder.cs +++ b/TUnit.Engine/Building/TestBuilder.cs @@ -1126,6 +1126,16 @@ private Task InvokeDiscoveryEventReceiversAsync(TestContext context) context.Metadata.TestDetails.TestName, context); + // Expose the resolved method argument values as filterable properties so + // callers can use `--treenode-filter` with syntax like `MyTest[arg0=*foo*]` + // to target a specific parameterized instance. + var testMethodArguments = context.Metadata.TestDetails.TestMethodArguments; + for (var i = 0; i < testMethodArguments.Length; i++) + { + var argValue = testMethodArguments[i]?.ToString() ?? ""; + discoveredContext.AddProperty($"arg{i}", argValue); + } + return _eventReceiverOrchestrator.InvokeTestDiscoveryEventReceiversAsync(context, discoveredContext, CancellationToken.None); } diff --git a/TUnit.TestProject/FilterByArgumentValueTests.cs b/TUnit.TestProject/FilterByArgumentValueTests.cs new file mode 100644 index 0000000000..33cad8b1d0 --- /dev/null +++ b/TUnit.TestProject/FilterByArgumentValueTests.cs @@ -0,0 +1,12 @@ +namespace TUnit.TestProject; + +public class FilterByArgumentValueTests +{ + [Test] + [Arguments("alpha")] + [Arguments("beta")] + [Arguments("gamma")] + public void Test(string value) + { + } +}