Skip to content

Migrate from xUnit v2 to xUnit v3 with MTP support#2918

Draft
slang25 wants to merge 11 commits intoApp-vNext:mainfrom
slang25:xunitv3
Draft

Migrate from xUnit v2 to xUnit v3 with MTP support#2918
slang25 wants to merge 11 commits intoApp-vNext:mainfrom
slang25:xunitv3

Conversation

@slang25
Copy link
Copy Markdown
Contributor

@slang25 slang25 commented Feb 3, 2026

Summary

This PR migrates the test infrastructure from xUnit v2 to xUnit v3 with Microsoft Testing Platform (MTP) support.

Changes

Package Updates:

  • Replace xunit (2.9.3) and xunit.runner.visualstudio (3.1.5) with xunit.v3.mtp-v2 (3.2.2)
  • Update FsCheck.Xunit to FsCheck.Xunit.v3
  • Add Microsoft.Testing.Platform (2.1.0) explicit version pin
  • Stryker dotnet-stryker already at 4.14.0 which includes MTP coverage support

Build Configuration:

  • Add <OutputType>Exe</OutputType> to all test projects (required by xUnit v3)
  • Enable Microsoft Testing Platform in global.json
  • Configure Stryker to use MTP test runner ("test-runner": "mtp")
  • Run test executables directly via dotnet exec instead of dotnet test, working around known SDK bug with dotnet test --project in MTP mode (Validate historical hacks for test projects that may be harmful for MTP projects dotnet/sdk#49063)
  • Use --report-spekt-junit for JUnit XML reporting (MTP-compatible flag from JunitXml.TestLogger)

Code Changes:

  • Update ITestOutputHelper implementations to include new Output property and Write methods
  • Fix TheoryData type inference issues with explicit casts
  • Use TestContext.Current.CancellationToken for test cancellation
  • Remove obsolete Xunit.Abstractions using directives

Notes

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 3, 2026

⚠️ JUnit XML file not found

The CLI was unable to find any JUnit XML files to upload.
For more help, visit our troubleshooting guide.

@martincostello
Copy link
Copy Markdown
Member

Will Stryker actually get the coverage with MTP yet? I thought that hadn't been implemented yet.

@Frulfump
Copy link
Copy Markdown

Frulfump commented Feb 3, 2026

Will Stryker actually get the coverage with MTP yet? I thought that hadn't been implemented yet.

I don't think so based on this comment from yesterday stryker-mutator/stryker-net#3094 (comment)

please also let us know any results you're seeing such as execution speed, differences in results etc. Missing coverage analysis mostly means that we are missing an optimalization(sic) and that we are not able to give you the same detail level in reports, but should not have a a negative impact on accuracy (actually running without coverage analysis is the most accurate since we don't skip any unit tests).

There are accuracy problems for other reasons, for example we don't yet properly support static context so if you use static context in your source code your mutation result may be drastically wrong in some places.

Maybe it's not a problem as per the comment but you know better of course,

@martincostello
Copy link
Copy Markdown
Member

  • There is a build warning about GitHubActionsTestLogger not being strong-named, but this is non-blocking

This should have been fixed by #3000, so the changes related to strong naming should be able to be removed.

Comment thread eng/Test.targets Outdated
Comment thread Directory.Packages.props Outdated
Comment thread eng/stryker-config.json Outdated
@martincostello
Copy link
Copy Markdown
Member

I'm moving this back to draft as stryker is very broken, but doesn't error so the green CI legs are lies.

@martincostello martincostello marked this pull request as draft March 25, 2026 20:48
Comment thread cake.cs Outdated
@slang25 slang25 marked this pull request as ready for review April 10, 2026 21:00
@slang25 slang25 force-pushed the xunitv3 branch 2 times, most recently from b6fbc70 to 5d79e37 Compare April 10, 2026 22:17
- Replace xunit 2.9.3 and xunit.runner.visualstudio with xunit.v3.mtp-v2 3.2.2
- Update FsCheck.Xunit to FsCheck.Xunit.v3
- Add Microsoft.Testing.Platform 2.1.0 and enable MTP in global.json
- Add OutputType Exe to test projects (required by xUnit v3)
- Configure Stryker with test-runner mtp for coverage support
- Run test executables directly via dotnet exec, bypassing
  dotnet test --project MSBuild discovery bug (dotnet/sdk#49063)
- Update ITestOutputHelper implementations for new xUnit v3 interface
- Fix TheoryData type inference with explicit casts
- Use TestContext.Current.CancellationToken for test cancellation
- Remove obsolete Xunit.Abstractions using directives

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Patch Stryker's MTP test runner to fix three bugs that caused ~55% of
mutations to incorrectly survive:

1. Only "failed" execution state was counted as test failure, not "error"
2. EveryTest() sentinel was lost during test result accumulation when
   the MTP server crashed (GetIdentifiers() returns empty for sentinel)
3. Static field initializer mutations were never killed because the MTP
   process reuses the same process across mutations, and static fields
   initialize only once

Also add eng/AssemblyVersion.cs to preserve the assembly version in
Stryker-compiled assemblies (Stryker skips auto-generated files which
caused version mismatch: 1.0.0.0 vs expected 8.0.0.0).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@slang25 slang25 marked this pull request as draft April 11, 2026 14:07
slang25 and others added 9 commits April 11, 2026 15:09
Three issues with the PatchStryker Cake task:
1. Patch file didn't match actual v4.14.1 source (regenerated)
2. Path resolution used wrong working directory (use script-relative path)
3. Build ran from wrong working directory (NuGet packages not found)

Also change target-framework from net10.0 to net8.0 since source projects
don't target net10.0, causing Stryker framework mismatch on CI.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The AssemblyVersion.cs file was unconditionally included via Library.targets,
causing build failures in VB.NET and F# projects that import it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MTP test runner has higher per-session overhead than vstest due to
JSON-RPC server process startup per mutation. The legacy mutation job
(889 mutants, 2113 tests) needs ~80-90 minutes with MTP vs ~6 minutes
with vstest.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The legacy Polly.csproj targets net6.0 (no net8.0), causing a framework
mismatch when Stryker's MTP runner tried to test mutated net6.0 assemblies
in a net8.0 test host. This caused mutation testing to deadlock with zero
progress output. Removing target-framework lets Stryker auto-detect the
correct framework per project, matching the upstream main branch config.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The legacy Polly.csproj targets net6.0 (no net8.0) while Polly.Specs
targets net8.0+. This TFM mismatch causes Stryker's MTP runner to
deadlock during mutation testing — the MTP server process hangs without
sending completion signals, blocking all runners indefinitely.

Use the default (vstest) test runner for the legacy project only. The
test project still uses xUnit v3 with MTP natively; only Stryker's
communication protocol changes for this one project.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MTP runner works locally with concurrency 4 but deadlocks on CI
(ubuntu-latest, 2 CPUs). Adding --verbosity debug --log-to-file to
capture detailed Stryker logs for the legacy project. The logs will be
included in the mutation-report artifact for analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The legacy mutation run (889 mutants × 2113 tests) takes ~79 minutes,
exceeding the 60-minute timeout. Stryker's dots reporter is buffered on
CI so no output appears until the run completes, making timeouts look
like deadlocks. Also removes debug logging added for investigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants