The new Microsoft Testing Platform for .NET: An introduction with practical samples and migration guidance

Testing in .NET has historically been associated with VSTest. That choice was reasonable for a long time because VSTest offered broad tooling support, deep IDE integration and a familiar execution model for MSTest, xUnit and NUnit projects. But the .NET ecosystem has changed. Native AOT, trimming, simplified deployment models, executable-first workflows and more explicit build-time configuration have pushed the platform toward models that do not fit particularly well with the older test infrastructure.

Microsoft Testing Platform, often abbreviated as MTP, is Microsoft’s answer to that shift. It is not a new unit testing framework like MSTest, xUnit or NUnit. It is a new test platform: the runtime and orchestration layer that discovers, hosts and executes tests, integrates with tools and exposes extension points.

That distinction matters because much of the confusion around the topic comes from mixing up framework and platform concerns. MSTest, NUnit, xUnit and TUnit define how tests are written. Microsoft Testing Platform defines how those tests are run.

This article explains what Microsoft Testing Platform is, what is actually new, why Microsoft introduced it, where the advantages are real, where migration friction can appear and how the platform looks in practice with concrete samples. The goal is to make the topic accessible for beginners without flattening the details that matter to experienced .NET engineers.

Introduction

In .NET, a test setup has two main layers:

  1. The test framework, such as MSTest, NUnit, xUnit.net or TUnit.
  2. The test platform, which discovers tests, runs them and communicates with CLI tools, IDEs and CI systems.

For many years, VSTest was the default platform. Microsoft Testing Platform is a newer alternative designed for modern .NET scenarios. According to the official documentation, it is a lightweight and portable alternative to VSTest and is embedded directly in the test project itself.

That last point changes the mental model significantly. Instead of relying on a more centralized runtime setup, the test runner becomes part of the test application. In practice, this means that MTP-based test projects behave more like regular executables. They can be run directly, via dotnet run, via dotnet exec, through IDE integration and in CI, while still exposing testing-specific functionality.

The official platform overview describes several architectural pillars behind this design:

  • determinism
  • runtime transparency
  • compile-time registration of extensions
  • zero dependencies in the platform core
  • hostability in any .NET application
  • support for modern .NET form factors, including Native AOT scenarios
  • a deliberately performance-oriented orchestration model

That is the conceptual baseline for everything else in the platform.

What Is New and Why

Microsoft Testing Platform is not simply a rebranding of VSTest. The differences are architectural.

The platform is embedded into the test project

The most visible difference is that the runner is embedded in the test project. The official documentation explicitly states that there are no extra application dependencies such as vstest.console required to run tests, even though dotnet test can still be used.

For beginners, this can be summarized as follows: an MTP-based test project behaves more like a normal .NET application with test semantics attached to it.

For advanced users, the more important implication is that the execution model is less dependent on historical layering decisions in VSTest.

Compile-time registration replaces more dynamic behavior

A core design goal of MTP is determinism. Extensions are registered at compile time instead of relying on a more dynamic discovery model. This is intended to reduce context-dependent behavior and make local execution, CI execution and IDE execution more consistent.

That change is also one of the reasons Microsoft emphasizes runtime transparency. The platform avoids interfering with test framework code through reflection-heavy coordination, isolated loading models or custom assembly resolution behavior where possible.

The platform is designed around modern .NET constraints

The official comparison material highlights a few scenarios where MTP is clearly the better fit:

  • Native AOT and trimming related test execution scenarios
  • executable-first workflows such as dotnet run
  • stricter and more explicit defaults
  • modern extension composition through NuGet packages

These are not niche details. They are signals that the test platform is being aligned with the rest of the current .NET engineering model.

The ecosystem is moving, but not all tools move at the same speed

This is where nuance becomes important. Microsoft Testing Platform is supported in Visual Studio and Visual Studio Code scenarios, but VSTest still has the broader compatibility history across the ecosystem. Microsoft says this directly in the platform comparison: VSTest remains the longer-established option and some third-party integrations can still lag behind MTP.

That means the new platform exists for strong architectural reasons, but migration should still be decided based on the shape of the solution and the surrounding tooling.

Benefits

The benefits of Microsoft Testing Platform are easiest to understand when separated into practical categories.

1. A lighter execution model

Microsoft describes MTP as lightweight and the MSTest documentation goes further by stating that the MSTest runner built on MTP is more lightweight and faster than VSTest.

This should not be interpreted as a universal benchmark claim for every solution and every framework combination. But it is a strong signal about the platform’s design goals: less runtime complexity, fewer moving parts and less orchestration overhead.

2. Better alignment with modern .NET application behavior

Because MTP-based test projects behave like executables, they fit naturally into workflows that already exist for regular applications:

  • direct execution of the built test binary
  • dotnet run for local execution
  • dotnet exec for explicit runtime execution
  • repository-level configuration through MSBuild properties and global.json

This reduces the conceptual gap between “application projects” and “test projects”.

3. Deterministic extension registration

The platform uses compile-time extension registration and transitive MSBuild integration. For many setups, installing an extension NuGet package is enough for it to be auto-detected and auto-registered.

That is an important maintainability gain. Instead of depending on loosely coordinated runtime discovery, the platform can describe more of its own behavior at build time.

4. Better fit for Native AOT and future .NET form factors

The test platform overview explicitly positions MTP as the right choice when Native AOT or trimming-related scenarios matter. That alone makes it strategically important for teams that want their tooling to evolve with the runtime.

5. More explicit behavior in edge cases

MTP is stricter in some situations. One example from the migration guide is especially important: if a test assembly runs zero tests, VSTest exits successfully, while MTP fails with exit code 8.

This is not a regression in itself. It is a different philosophy. Silent success for zero discovered tests can hide configuration mistakes, missing filters or broken discovery logic. Failing early is often the safer behavior in CI.

6. Extension-driven reporting and diagnostics

In VSTest, capabilities such as logging and collection were often consumed through generalized switches like --logger and --collect. In MTP, those concerns are modeled more explicitly through extension-specific options.

That design is more modular, but it also requires more deliberate migration work because old commands do not always translate one-to-one.

Practical Samples

The most effective way to understand MTP is to look at concrete project shapes.

Sample 1: A new MSTest project using MSTest.Sdk

The recommended approach for new MSTest projects is MSTest.Sdk. Microsoft states that this SDK provides the first-class MSTest experience and uses Microsoft Testing Platform by default unless explicitly configured otherwise.

1<Project Sdk="MSTest.Sdk/4.1.0">
2
3  <PropertyGroup>
4    <TargetFramework>net10.0</TargetFramework>
5    <ImplicitUsings>enable</ImplicitUsings>
6    <Nullable>enable</Nullable>
7  </PropertyGroup>
8
9</Project>

This is the cleanest entry point for a new codebase because the platform and runner choices are aligned from the beginning.

Sample 2: Opting an existing MSTest project into MTP

For an existing MSTest project that still uses the classic SDK style, the migration can be introduced through explicit MSBuild properties.

 1<Project Sdk="Microsoft.NET.Sdk">
 2
 3  <PropertyGroup>
 4    <TargetFramework>net10.0</TargetFramework>
 5    <ImplicitUsings>enable</ImplicitUsings>
 6    <Nullable>enable</Nullable>
 7
 8    <OutputType>Exe</OutputType>
 9    <EnableMSTestRunner>true</EnableMSTestRunner>
10    <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
11    <TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
12  </PropertyGroup>
13
14  <ItemGroup>
15    <PackageReference Include="MSTest" Version="4.1.0" />
16  </ItemGroup>
17
18</Project>

Three things are worth calling out here:

  1. OutputType changes to Exe because the test project becomes executable.
  2. EnableMSTestRunner opts the project into the MSTest runner on top of MTP.
  3. TestingPlatformDotnetTestSupport is relevant when dotnet test is used through the VSTest-based mode available in older SDK flows.

Sample 3: Repository-wide configuration in Directory.Build.props

Microsoft recommends placing these properties in Directory.Build.props rather than copying them into every test project. That advice is important because mixing VSTest-based and MTP-based .NET test projects in the same solution is explicitly documented as unsupported.

1<Project>
2
3  <PropertyGroup>
4    <EnableMSTestRunner>true</EnableMSTestRunner>
5    <TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
6    <TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
7  </PropertyGroup>
8
9</Project>

For larger repositories, this is usually the safer migration shape because it reduces configuration drift.

Sample 4: Native MTP mode for dotnet test in .NET 10+

Starting with the .NET 10 SDK, dotnet test can run in a native Microsoft Testing Platform mode. The official migration guide explains that this is enabled via global.json.

1{
2  "test": {
3    "runner": "Microsoft.Testing.Platform"
4  }
5}

This is a meaningful change because the older compatibility mode relied on VSTest infrastructure underneath dotnet test. In native MTP mode, the command line model becomes cleaner and the extra argument separator is no longer required.

Sample 5: Running tests directly

An MTP-enabled MSTest project can be executed in several ways:

1dotnet run --project Contoso.MyTests -- --filter "FullyQualifiedName~UnitTest1|TestCategory=CategoryA"
1dotnet exec Contoso.MyTests.dll --settings config.runsettings
1dotnet Contoso.MyTests.dll --settings config.runsettings
1Contoso.MyTests.exe --settings config.runsettings

This executable-first model is one of the most concrete differences from historical VSTest-centric habits.

Sample 6: TRX reporting and code coverage in MTP style

In VSTest, old command lines often looked like this:

1dotnet test --logger trx --collect "Code Coverage;Format=cobertura"

In MTP, the model becomes extension-specific. The migration guide maps those concepts like this:

1dotnet test --report-trx --coverage --coverage-output-format cobertura

When the compatibility layer for older dotnet test behavior is used, the extra -- separator is required:

1dotnet test -- --report-trx --coverage --coverage-output-format cobertura

That is more than a syntax change. It reflects the underlying architectural shift from generalized VSTest extensibility to explicit MTP extension commands.

Migration

Migration is straightforward in the simple case and subtle in the real-world case.

Step 1: Decide whether migration is appropriate now

The official platform comparison is pragmatic about trade-offs.

MTP is a strong fit when:

  • Native AOT or trimming-related scenarios matter
  • test projects should behave like normal executables
  • stricter and more deterministic behavior is preferred
  • the repository is mostly .NET and does not rely on mixed-language VSTest adapters

VSTest can still be the better choice when:

  • established third-party tooling is heavily coupled to VSTest
  • mixed-language adapter scenarios matter
  • a solution depends on project types not currently supported by MTP

Migration is not a mandatory modernization exercise. It is a platform decision.

Step 2: Standardize at repository level

Microsoft explicitly recommends not mixing VSTest and MTP-based .NET test projects in the same solution or run configuration. This is one of the most important migration rules.

If migration happens, it should usually happen at the repository or solution boundary, not project-by-project in a partially migrated state.

Step 3: Update the framework-specific opt-in

The migration guide lists the framework-level switches:

  • MSTest: EnableMSTestRunner
  • NUnit: EnableNUnitRunner
  • xUnit.net v3: UseMicrosoftTestingPlatformRunner

All test projects also need:

  • OutputType set to Exe

That is the baseline configuration change across frameworks.

Step 4: Decide how dotnet test is supposed to work

This is where many migrations become confusing.

For .NET 9 and earlier, MTP support in dotnet test is not native. It is built on top of the older VSTest mode, which means:

  • TestingPlatformDotnetTestSupport must be enabled
  • platform arguments must be separated with --

Example:

1dotnet test --no-build -- --list-tests

For .NET 10 and later, native MTP mode can be enabled in global.json and the extra -- is no longer needed.

That means the same repository can have very different command line ergonomics depending on the SDK strategy.

Step 5: Rewrite VSTest-specific arguments

This is the part that tends to be underestimated.

Some arguments map cleanly:

VSTestMicrosoft Testing Platform
--list-tests--list-tests
--diag--diagnostic
--results-directory--results-directory

Some arguments change meaning or extension model:

VSTestMicrosoft Testing Platform
--logger trx--report-trx
--collect "Code Coverage;Format=cobertura"--coverage --coverage-output-format cobertura
--blame-crash--crashdump with the relevant extension
--blame-hang--hangdump with the relevant extension

Some behaviors depend on the selected framework:

  • MSTest and NUnit can continue using familiar filter formats
  • xUnit on MTP uses the xUnit v3 query/filter model instead of the VSTest filter format

This is precisely why migration work should include CI scripts, local developer commands, IDE assumptions and documentation, not only .csproj changes.

Step 6: Account for behavioral differences

At least three differences are important enough to check during migration:

  1. Zero discovered tests can fail the build with exit code 8.
  2. Some output handling behaviors differ because MTP preserves console encoding more explicitly in scenarios where VSTest isolation historically behaved differently.
  3. Old RunSettings usage is no longer a core platform concept. MTP replaces that with testconfig.json, although MSTest and NUnit still support --settings for existing RunSettings-based workflows.

These are not theoretical details. They affect CI reliability, test discovery expectations and failure analysis.

Step 7: Update CI pipelines deliberately

The migration guide specifically calls out Azure DevOps. If a pipeline still uses the VSTest task, Microsoft recommends moving to the .NET CLI task.

A minimal Azure DevOps style example for TRX reporting with MTP looks like this:

1- task: DotNetCoreCLI@2
2  displayName: Run unit tests
3  inputs:
4    command: test
5    arguments: '-- --report-trx --results-directory $(Agent.TempDirectory)'

For GitHub Actions or other CI systems, the same principle applies: MTP changes test arguments and in some cases exit-code expectations. Pipelines should be reviewed as part of the migration, not after it.

Result: Is It Actually Faster?

This is the question most teams eventually ask.

The documented answer is careful but meaningful.

Microsoft describes the platform as lightweight and performance-oriented. The MSTest runner documentation makes the clearest statement: the MSTest runner built on MTP is more lightweight and faster than VSTest.

That is the official claim. But it does not automatically answer how much faster a specific solution will become.

What can be stated confidently

The following points are grounded in the documentation:

  • MTP is designed to reduce orchestration overhead.
  • The platform avoids some of the older dynamic behaviors that made the VSTest model more complex.
  • Extension registration is more explicit and build-time driven.
  • The runtime model is shaped for modern .NET execution scenarios.

All of those factors are consistent with lower startup and coordination overhead.

What remains project-specific

Actual end-to-end execution time still depends on many variables:

  • test framework in use
  • number of test projects
  • extension packages in the run
  • amount of console or diagnostic output
  • code coverage settings
  • CI machine performance
  • whether tests are CPU-bound, I/O-bound or process-heavy

For many repositories, the most realistic expectation is not “all tests suddenly run dramatically faster” but rather a combination of:

  • somewhat lower runner overhead
  • a cleaner execution model
  • better consistency between local and CI behavior
  • simpler long-term alignment with modern .NET scenarios

That is still valuable. In many engineering organizations, determinism and configuration clarity save more time than a small raw execution-time gain.

A practical conclusion on speed

If the question is whether MTP can be faster than VSTest, the answer is yes and Microsoft explicitly says so for the MSTest runner.

If the question is whether every repository will see a dramatic measurable improvement, the answer is no. The main benefits are architectural first, operational second and raw speed third.

That ordering is important because it keeps migration decisions honest.

Where Migration Can Fail

The most common failure patterns are structural rather than syntactic.

Mixing platforms in one solution

This is explicitly unsupported for .NET test projects in the same solution or run configuration. A half-migrated repository can behave worse than a repository that stays entirely on VSTest for one more release cycle.

Assuming dotnet test behaves the same everywhere

The command line model differs between the compatibility path used by older SDK workflows and the native MTP path in .NET 10+. This affects documentation, local scripts and CI definitions.

Forgetting extensions during command migration

Commands like --report-trx, --coverage, --crashdump or --hangdump depend on the relevant MTP extensions being installed. Old VSTest habits often hide this dependency behind more generalized arguments.

Treating filter migration as trivial

MSTest and NUnit preserve more continuity here. xUnit on MTP does not. Any solution with heavy --filter usage in xUnit should budget time for command rewriting and documentation updates.

Ignoring stricter exit behavior

Teams that accidentally rely on “zero tests is still success” semantics often discover this only after CI begins to fail. In most cases that failure is useful, but it still has to be planned for.

Conclusion

Microsoft Testing Platform is one of the more important infrastructure changes in the modern .NET testing story because it updates the test runtime model rather than merely adding new syntax on top of old assumptions.

For beginners, the core message is simple: MTP is the engine that runs tests, not the framework used to write them. It makes test projects behave more like normal .NET executables and introduces a more explicit model for configuration, extensions and execution.

For advanced teams, the more interesting message is architectural: MTP is designed around determinism, compile-time composition, modern .NET deployment shapes and a lighter orchestration model. That makes it especially relevant for repositories that want to reduce legacy testing friction and align with the direction of the .NET runtime.

Migration is not always urgent and VSTest remains a valid choice when ecosystem compatibility is still the dominant requirement. But for new test suites or for .NET-heavy repositories that want a cleaner and more future-aligned platform, Microsoft Testing Platform is increasingly the more coherent choice.

The strongest reason to adopt it is not that it promises magic speed gains. The strongest reason is that it gives .NET testing a runtime model that better matches how the platform itself now works.


Let's Work Together

Looking for an experienced Platform Architect or Engineer for your next project? Whether it's cloud migration, platform modernization or building new solutions from scratch - I'm here to help you succeed.

New Platforms
Modernization
Training & Consulting

Comments

Twitter Facebook LinkedIn WhatsApp