Skip to content

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:

global.json
{ "test": { "runner": "Microsoft.Testing.Platform" } }

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:

dotnet test --project <path-to-test-project> --report-trx

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:

<PackageVersion Include="xunit.v3" Version="3.2.2" />

If the test project manages its own package versions, update its .csproj file:

<PackageReference Include="xunit.v3" Version="3.2.2" />

Restore packages and commit the updated lock file if the repository uses one:

dotnet restore

🔬 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.