You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[test-improver] Add unit tests for ConsoleTestExecutionFilterFactory #11873
ConsoleTestExecutionFilterFactory (src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionFilterFactory.cs) is a small but real production class wired into ConsoleTestHost that decides which ITestExecutionFilter to hand to the test run: TreeNodeFilter when --treenode-filter is set, TestNodeUidListFilter when --filter-uid is set, NopFilter when neither is set, and an unreachable-guard throw when both are set simultaneously. It had zero direct unit tests — only indirectly exercised through acceptance tests that pass real CLI arguments end-to-end.
Approach
Added ConsoleTestExecutionFilterFactoryTests.cs with 4 tests mocking ICommandLineOptions (same Moq pattern used elsewhere in this test project, e.g. ArgumentArityTests.cs) to drive all four branches of the switch directly:
Neither option set → NopFilter.
Only --treenode-filter set → TreeNodeFilter constructed with the exact pattern string.
Only --filter-uid set → TestNodeUidListFilter containing the exact UIDs, in order.
Both options set → throws (matching ApplicationStateGuardTests's cross-TFM-safe pattern of asserting on UnreachableException's full type name, since the type is an internal polyfill on non-.NETCoreApp TFMs).
Coverage impact
Metric
Before
After
Microsoft.Testing.Platform.UnitTests total (net8.0)
2866
2870
Microsoft.Testing.Platform.UnitTests total (net9.0)
2867
2871
Failed
0
0
Skipped
22 (pre-existing)
22 (pre-existing)
Trade-offs
Low maintenance burden: pure unit tests against a sealed, 4-branch class with a stable public contract (ITestExecutionFilterFactory.TryCreateAsync()); no new test infrastructure or dependencies.
Build succeeded (0 warnings) on net8.0 and net9.0. Full Microsoft.Testing.Platform.UnitTests suite passes on both TFMs, no regressions. dotnet format whitespace TestFx.slnx --verify-no-changes clean for the new file (only the expected, pre-existing F#-project warning noted).
Effectiveness evidence
Behavior and inputs
Independent expectation and source
Wrong implementation and distinguishing assertion
Real path and boundary/locale coverage
Evidence
TryCreateAsync() when only --treenode-filter "/Assembly/Namespace/Class/Method" is set
Per the method's own switch contract (doc comment on ITestExecutionFilterFactory/method body): (true, false) branch must construct new TreeNodeFilter(treenodeFilter![0]) — independently derived from reading the production switch arms, not by calling the method itself
Swapped (true, false) arm to return new NopFilter() instead of new TreeNodeFilter(...) — assertion Assert.IsInstanceOfType<TreeNodeFilter>(filter) rejects it
Exercises the real ConsoleTestExecutionFilterFactory.TryCreateAsync() via mocked ICommandLineOptions.TryGetOptionArgumentList, the same entry point ConsoleTestHost uses; also covers the "neither set" (NopFilter), "only UID set" (TestNodeUidListFilter with exact ordered UIDs), and "both set" (throws) boundary branches
Executed red/green: injected the (true, false) fault above, rebuilt (dotnet build ... -f net8.0), ran the full suite — TryCreateAsync_WhenOnlyTreeNodeFilterIsSet_ReturnsTreeNodeFilterWithGivenPattern failed exactly as expected (actual type: NopFilter), 1/2870 failed. Reverted the fault, rebuilt, reran — 0/2870 failed (net8.0) and 0/2871 failed (net9.0), 22 pre-existing skips, no regressions.
Note
This was originally intended as a pull request, but the git push operation failed.
Original error: The process '/usr/bin/git' failed with exit code 128
🤖 Automated content by GitHub Copilot. Generated by the Test Improver workflow. · copilot · auto · 414.2 AIC · ⌖ 15.7 AIC · ⊞ 22.1K · [◷]( · ◷) Comment /test-assist to run again
Add this agentic workflow to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/test-improver.md@main
Goal and rationale
ConsoleTestExecutionFilterFactory(src/Platform/Microsoft.Testing.Platform/Requests/ConsoleTestExecutionFilterFactory.cs) is a small but real production class wired intoConsoleTestHostthat decides whichITestExecutionFilterto hand to the test run:TreeNodeFilterwhen--treenode-filteris set,TestNodeUidListFilterwhen--filter-uidis set,NopFilterwhen neither is set, and an unreachable-guard throw when both are set simultaneously. It had zero direct unit tests — only indirectly exercised through acceptance tests that pass real CLI arguments end-to-end.Approach
Added
ConsoleTestExecutionFilterFactoryTests.cswith 4 tests mockingICommandLineOptions(sameMoqpattern used elsewhere in this test project, e.g.ArgumentArityTests.cs) to drive all four branches of theswitchdirectly:NopFilter.--treenode-filterset →TreeNodeFilterconstructed with the exact pattern string.--filter-uidset →TestNodeUidListFiltercontaining the exact UIDs, in order.ApplicationStateGuardTests's cross-TFM-safe pattern of asserting onUnreachableException's full type name, since the type is an internal polyfill on non-.NETCoreAppTFMs).Coverage impact
Microsoft.Testing.Platform.UnitTeststotal (net8.0)Microsoft.Testing.Platform.UnitTeststotal (net9.0)Trade-offs
Low maintenance burden: pure unit tests against a sealed, 4-branch class with a stable public contract (
ITestExecutionFilterFactory.TryCreateAsync()); no new test infrastructure or dependencies.Reproducibility
(repeat with
-f net9.0for the second TFM)Test Status
Build succeeded (0 warnings) on net8.0 and net9.0. Full
Microsoft.Testing.Platform.UnitTestssuite passes on both TFMs, no regressions.dotnet format whitespace TestFx.slnx --verify-no-changesclean for the new file (only the expected, pre-existing F#-project warning noted).Effectiveness evidence
TryCreateAsync()when only--treenode-filter "/Assembly/Namespace/Class/Method"is setswitchcontract (doc comment onITestExecutionFilterFactory/method body):(true, false)branch must constructnew TreeNodeFilter(treenodeFilter![0])— independently derived from reading the production switch arms, not by calling the method itself(true, false)arm to returnnew NopFilter()instead ofnew TreeNodeFilter(...)— assertionAssert.IsInstanceOfType<TreeNodeFilter>(filter)rejects itConsoleTestExecutionFilterFactory.TryCreateAsync()via mockedICommandLineOptions.TryGetOptionArgumentList, the same entry pointConsoleTestHostuses; also covers the "neither set" (NopFilter), "only UID set" (TestNodeUidListFilterwith exact ordered UIDs), and "both set" (throws) boundary branches(true, false)fault above, rebuilt (dotnet build ... -f net8.0), ran the full suite —TryCreateAsync_WhenOnlyTreeNodeFilterIsSet_ReturnsTreeNodeFilterWithGivenPatternfailed exactly as expected (actual type: NopFilter), 1/2870 failed. Reverted the fault, rebuilt, reran — 0/2870 failed (net8.0) and 0/2871 failed (net9.0), 22 pre-existing skips, no regressions.Note
This was originally intended as a pull request, but the git push operation failed.
Original error: The process '/usr/bin/git' failed with exit code 128
Workflow Run: View run details and download bundle artifact
The bundle file is available in the
agentartifact in the workflow run linked above.Create the pull request manually
Warning
Firewall blocked 93 domains
The following domains were blocked by the firewall during workflow execution:
0t3vsblobprodcus362.vsblob.vsassets.io11vvsblobprodcus336.vsblob.vsassets.io1javsblobprodcus364.vsblob.vsassets.io1k9vsblobprodcus379.vsblob.vsassets.io1oavsblobprodcus350.vsblob.vsassets.io1p7vsblobprodcus324.vsblob.vsassets.io1s1vsblobprodcus386.vsblob.vsassets.io2kmvsblobprodcus39.vsblob.vsassets.io2zrvsblobprodcus388.vsblob.vsassets.io37bvsblobprodcus311.vsblob.vsassets.io37cvsblobprodcus359.vsblob.vsassets.io4m6vsblobprodcus384.vsblob.vsassets.io4myvsblobprodcus32.vsblob.vsassets.io4vyvsblobprodcus361.vsblob.vsassets.io4zjvsblobprodcus390.vsblob.vsassets.io51yvsblobprodcus36.vsblob.vsassets.io5dkvsblobprodcus355.vsblob.vsassets.io5rqvsblobprodcus385.vsblob.vsassets.io6s7vsblobprodcus313.vsblob.vsassets.io7devsblobprodcus323.vsblob.vsassets.io7k6vsblobprodcus337.vsblob.vsassets.io7tjvsblobprodcus341.vsblob.vsassets.io80zvsblobprodcus35.vsblob.vsassets.io8xbvsblobprodcus382.vsblob.vsassets.io96bvsblobprodcus338.vsblob.vsassets.ioajhvsblobprodcus363.vsblob.vsassets.iobcnvsblobprodcus378.vsblob.vsassets.iobuilds.dotnet.microsoft.comc50vsblobprodcus330.vsblob.vsassets.ioc78vsblobprodcus322.vsblob.vsassets.iocflvsblobprodcus383.vsblob.vsassets.ioci.dot.netckzvsblobprodcus347.vsblob.vsassets.iod0svsblobprodcus381.vsblob.vsassets.iodata.nuget.orgdlbvsblobprodcus316.vsblob.vsassets.ioe7bvsblobprodcus348.vsblob.vsassets.iofdpvsblobprodcus345.vsblob.vsassets.iofrdvsblobprodcus327.vsblob.vsassets.iogbsvsblobprodcus365.vsblob.vsassets.iogervsblobprodcus329.vsblob.vsassets.ioh6tvsblobprodcus346.vsblob.vsassets.ioi1qvsblobprodcus353.vsblob.vsassets.ioibzvsblobprodcus369.vsblob.vsassets.ioimzvsblobprodcus368.vsblob.vsassets.iojd4vsblobprodcus366.vsblob.vsassets.iojosvsblobprodcus372.vsblob.vsassets.iojrqvsblobprodcus343.vsblob.vsassets.iok0ivsblobprodcus356.vsblob.vsassets.iok4kvsblobprodcus344.vsblob.vsassets.iokgfvsblobprodcus314.vsblob.vsassets.iokh4vsblobprodcus325.vsblob.vsassets.iokijvsblobprodcus387.vsblob.vsassets.iokmuvsblobprodcus389.vsblob.vsassets.iokxqvsblobprodcus376.vsblob.vsassets.iol49vsblobprodcus358.vsblob.vsassets.iol7avsblobprodcus319.vsblob.vsassets.ioljcvsblobprodcus317.vsblob.vsassets.iolylvsblobprodcus31.vsblob.vsassets.iom16vsblobprodcus374.vsblob.vsassets.iom6xvsblobprodcus342.vsblob.vsassets.iom8dvsblobprodcus37.vsblob.vsassets.iomfjvsblobprodcus373.vsblob.vsassets.ion3kvsblobprodcus335.vsblob.vsassets.ionn8vsblobprodcus340.vsblob.vsassets.ioo3svsblobprodcus318.vsblob.vsassets.ioofvvsblobprodcus315.vsblob.vsassets.iop2ovsblobprodcus312.vsblob.vsassets.iopc2vsblobprodcus360.vsblob.vsassets.iopdfvsblobprodcus380.vsblob.vsassets.iope3vsblobprodcus354.vsblob.vsassets.iope4vsblobprodcus351.vsblob.vsassets.iopkgs.dev.azure.compkvvsblobprodcus321.vsblob.vsassets.iorcxvsblobprodcus328.vsblob.vsassets.ios4uvsblobprodcus326.vsblob.vsassets.ios8mvsblobprodcus38.vsblob.vsassets.iose1vsblobprodcus349.vsblob.vsassets.iosqdvsblobprodcus333.vsblob.vsassets.iost8vsblobprodcus339.vsblob.vsassets.iotphvsblobprodcus375.vsblob.vsassets.iou3hvsblobprodcus371.vsblob.vsassets.iou6ovsblobprodcus377.vsblob.vsassets.ioukkvsblobprodcus352.vsblob.vsassets.iouy6vsblobprodcus34.vsblob.vsassets.iov53vsblobprodcus320.vsblob.vsassets.iovb4vsblobprodcus33.vsblob.vsassets.iovwvvsblobprodcus334.vsblob.vsassets.iox3yvsblobprodcus370.vsblob.vsassets.ioxupvsblobprodcus332.vsblob.vsassets.ioyluvsblobprodcus367.vsblob.vsassets.ioyttvsblobprodcus357.vsblob.vsassets.ioytvvsblobprodcus310.vsblob.vsassets.ioTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.
Add this agentic workflow to your repo
To install this agentic workflow, run