VSTest target is no longer supported on .NET 10¶
One-sentence summary: a newer xunit.v3 release brings in Microsoft.Testing.Platform 2.x, which dropped VSTest support on .NET 10; migrate the project to native MTP, or pin xunit.v3 to 3.2.2 if it must stay on VSTest.
🚨 Symptom¶
The unit-test step fails in Azure DevOps even though the tests pass locally:
error : Testing with VSTest target is no longer supported by Microsoft.Testing.Platform on .NET 10 SDK and later. If you use dotnet test, you should opt-in to the new dotnet test experience. For more information, see https://aka.ms/dotnet-test-mtp-error
The failure occurs while MSBuild invokes the VSTest target for a .NET 10 test project.
📌 Applies to¶
| Aspect | Value |
|---|---|
| Component | Legacy VSTest-based .NET unit-test projects (no global.json MTP opt-in) |
| Forge versions | Forge 3 projects targeting .NET 10 that predate the native-MTP templates |
| Related versions | xunit.v3 newer than 3.2.2; observed with Microsoft.Testing.Platform 2.3.3 |
Apps generated from the current saif-feature-base template already ship a global.json with { "test": { "runner": "Microsoft.Testing.Platform" } } and pin xunit.v3 4.x, so they never hit this error. If you see it in such an app, the global.json opt-in is missing rather than the xunit.v3 version being wrong.
🧠 Cause¶
Updating xunit.v3 to 4.x brings in Microsoft.Testing.Platform 2.x, which dropped VSTest support entirely. Anything that still drives tests through the MSBuild VSTest target, including a plain dotnet test without the global.json opt-in, fails on the .NET 10 SDK. Local runs can pass when they use a different SDK or test execution path, so the incompatibility may appear only in a build.
Forge's own Azure DevOps pipeline templates no longer call dotnet test: they build each test project and run the built assembly directly under dotnet-coverage, so they are unaffected by the VSTest/MTP split. The global.json opt-in only changes local dotnet test invocations.
✅ Fix¶
Preferred: migrate the project to native MTP¶
Keep xunit.v3 on 4.x and opt the repo into the native dotnet test experience with a repo-root global.json:
Add a Microsoft.Testing.Extensions.TrxReport reference (MTP-2.x compatible, for example 2.4.0) to each test project that needs TRX output, and drop coverlet.collector and xunit.runner.visualstudio, both of which are VSTest-only. Coverage comes from dotnet-coverage, which instruments the test process and needs no package reference. This is exactly what the current templates scaffold; see Test Execution: Native Microsoft Testing Platform (MTP) and the dotnet-testing skill's VSTest-to-MTP migration notes.
Under MTP, dotnet test requires --project, --solution, or --test-modules instead of a positional project path:
Fallback: pin xunit.v3 to 3.2.2 on a legacy VSTest project¶
If the repository must keep running the VSTest test host, pin xunit.v3 to the last 3.x release instead of migrating. This is a holding position, not the recommended end state.
If the repository uses central package management, update Directory.Packages.props:
If the test project manages its own package versions, update its .csproj file:
Restore packages and commit the updated lock file if the repository uses one:
🔬 Verify¶
Re-run the build. The test step should complete without the Testing with VSTest target is no longer supported error. After migrating to MTP, dotnet test --project <path> --report-trx should discover and run tests locally; note that MTP treats a run that discovers zero tests as a failure (exit code 8), so give an intentionally empty test project a placeholder test.