{"id":57713,"date":"2025-08-21T10:05:00","date_gmt":"2025-08-21T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=57713"},"modified":"2025-10-14T19:45:09","modified_gmt":"2025-10-15T02:45:09","slug":"dotnet-test-with-mtp","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/dotnet-test-with-mtp\/","title":{"rendered":"Enhance your CLI testing workflow with the new dotnet test"},"content":{"rendered":"<p>The <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/tools\/dotnet-test\"><code>dotnet test<\/code><\/a> command is part of the .NET CLI and is used to run tests in a given solution. It supports multiple test frameworks, including MSTest, xUnit, NUnit and TUnit. The command recognizes two main test platforms: VSTest and Microsoft.Testing.Platform (MTP). Any test framework built on top of either VSTest or MTP is automatically supported. The command builds the solution and executes the tests, providing detailed results and diagnostics.<\/p>\n<p>In this blog post, we&#8217;ll explore the significant improvements to <code>dotnet test<\/code> in .NET 10 through Microsoft.Testing.Platform integration. We will cover:<\/p>\n<ul>\n<li><strong>Platform comparison<\/strong>: Understanding the differences between VSTest and Microsoft.Testing.Platform (MTP)<\/li>\n<li><strong>Evolution timeline<\/strong>: How <code>dotnet test<\/code> integration has progressed from .NET 9 to .NET 10<\/li>\n<li><strong>Key benefits<\/strong>: Performance improvements, enhanced diagnostics, and new capabilities enabled by MTP<\/li>\n<li><strong>Enhanced options<\/strong>: New and improved command-line options for flexible test execution<\/li>\n<li><strong>Practical examples<\/strong>: Real-world usage scenarios and configuration patterns<\/li>\n<\/ul>\n<p>Whether you&#8217;re looking to improve your testing workflow&#8217;s performance or access advanced features, this guide  will help you leverage the full potential of the new <code>dotnet test<\/code> experience.<\/p>\n<h2>VSTest vs. Microsoft.Testing.Platform (MTP)<\/h2>\n<p>Microsoft.Testing.Platform (MTP) is a lightweight and portable alternative to VSTest for running tests in all contexts, including continuous integration (CI) pipelines, CLI, Visual Studio Test Explorer, and VS Code Text Explorer. The Microsoft.Testing.Platform is embedded directly in your test projects, and there\u2019s no other app dependencies.<\/p>\n<p>For the latest updates on Microsoft.Testing.Platform adoption, see our recent <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/mtp-adoption-frameworks\/\">post<\/a>.<\/p>\n<p>To learn more about MTP vs VSTest, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/microsoft-testing-platform-vs-vstest\">Microsoft.Testing.Platform and VSTest comparison<\/a>.<\/p>\n<h2>Evolution of dotnet test with MTP<\/h2>\n<p>The integration of Microsoft.Testing.Platform with <code>dotnet test<\/code> has undergone significant improvements from .NET 9 to .NET 10. Understanding this evolution helps explain why the current approach delivers better performance and enhanced developer experience.<\/p>\n<h3>.NET 9 (Previous Approach)<\/h3>\n<p>In .NET 9, Microsoft.Testing.Platform could be used with <code>dotnet test<\/code> through VSTest mode. This required:<\/p>\n<ol>\n<li>Using the <code>--<\/code> separator to pass arguments<\/li>\n<li>Enabling the <code>TestingPlatformDotnetTestSupport<\/code> MSBuild property<\/li>\n<\/ol>\n<pre><code class=\"language-bash\">dotnet test -- --coverage --report-trx<\/code><\/pre>\n<p>While functional, this approach had several limitations that prompted improvements in .NET 10. For detailed information, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/microsoft-testing-platform-integration-dotnet-test\">Use Microsoft.Testing.Platform in the VSTest mode of dotnet test<\/a>.<\/p>\n<h3>.NET 10 (Current Approach)<\/h3>\n<p>.NET 10 introduces native integration with Microsoft.Testing.Platform. To enable it, specify the test runner in the <code>global.json<\/code> file.<\/p>\n<pre><code class=\"language-json\">{\n    \"test\": {\n        \"runner\": \"Microsoft.Testing.Platform\"\n    }\n}<\/code><\/pre>\n<p>This configuration eliminates the need for complex command-line arguments and provides seamless integration with the .NET SDK.<\/p>\n<p><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Migration Path<\/strong><\/p>The new approach simplifies test execution while providing better performance and diagnostics compared to the VSTest integration method.<\/div><\/p>\n<h2><code>dotnet test<\/code> with MTP<\/h2>\n<p>Here\u2019s a breakdown of the key benefits of using the Microsoft.Testing.Platform (MTP)\u00a0with\u00a0<code>dotnet test<\/code> over the VSTest\u00a0integration:<\/p>\n<p>\ud83c\udf10 Unified Test Execution<\/p>\n<p>MTP\u00a0is designed to be the future of test execution in .NET. It integrates more tightly with the .NET SDK and\u00a0dotnet test, offering a more consistent experience.<\/p>\n<p>\ud83d\udcca Better Performance<\/p>\n<p>MTP is optimized for performance, especially in large test suites. It reduces start times and improves test discovery and execution speed.<\/p>\n<p>\u2705 Improved Diagnostics and Logging<\/p>\n<p>MTP provides\u00a0richer diagnostics, including better structured logs. This makes it easier to troubleshoot flaky tests or performance issues.<\/p>\n<p>\u25b6\ufe0f Parallel Test Execution<\/p>\n<p>Execution of assemblies from different TFMs is supported with MTP, which is crucial for scaling test runs in CI pipelines.<\/p>\n<p>\ud83d\udce6 Dynamic Arguments<\/p>\n<p>The test-related arguments are no longer fixed, as they are tied to the registered extensions in the test project(s).<\/p>\n<p>\u2696\ufe0f\u00a0Globbing Filter<\/p>\n<p>It allows for flexible and efficient test selection by using patterns to include or exclude specific test assemblies, simplifying configuration and improving usability.<\/p>\n<p>\u2705 Improved UI\/UX<\/p>\n<p>The output is nicely formatted with the usage of ANSI terminal.<\/p>\n<p>To learn more about MTP and its foundational principles, see <a href=\"https:\/\/aka.ms\/mtp-overview\">Microsoft.Testing.Platform<\/a>.<\/p>\n<h3>See It in Action<\/h3>\n<p>Watch this demonstration of the enhanced <code>dotnet test<\/code> experience with Microsoft.Testing.Platform (MTP), showcasing how MTP&#8217;s native integration delivers performance gains, improved output formatting, and new command-line options in action:<\/p>\n<p><iframe width=\"800\" height=\"450\" src=\"https:\/\/www.youtube.com\/embed\/J3On_irDTns?si=wmgbFB5X-0u1UFVv\" allowfullscreen><\/iframe><\/p>\n<h2>Mixed Project Compatibility<\/h2>\n<p><div class=\"alert alert-info\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>All-or-Nothing Requirement<\/strong><\/p>\nIn a solution with MTP enabled via <strong>global.json<\/strong>, every test project must use Microsoft.Testing.Platform. Having any VSTest projects will prevent those projects from being able to run.\n<\/div><\/p>\n<p>This means you need to migrate all test projects in your solution to MTP before enabling the <code>global.json<\/code> setting. You cannot run a mixed environment where some projects use VSTest and others use MTP.<\/p>\n<h2>Project Properties Cleanup<\/h2>\n<p>With native MTP integration in .NET 10, several MSBuild properties from the previous approach are no longer needed and can be safely removed from your test projects:<\/p>\n<ul>\n<li><strong><code>TestingPlatformDotnetTestSupport<\/code><\/strong>: No longer required for MTP integration<\/li>\n<li><strong><code>TestingPlatformShowTestsFailure<\/code><\/strong>: Obsolete, as test failures are now displayed by default in the new experience<\/li>\n<\/ul>\n<p><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Clean Up Tip<\/strong><\/p>\nRemove these properties during migration to simplify your project files and avoid confusion with old configuration options.\n<\/div><\/p>\n<h2>Options<\/h2>\n<p>With Microsoft.Testing.Platform integration, <code>dotnet test<\/code> provides enhanced flexibility in how you target and execute tests. The following sections cover the various options available to customize your testing workflow, from basic execution to advanced filtering and configuration.<\/p>\n<h3>The Default Option<\/h3>\n<p>To run the tests in the project or solution in the current directory<\/p>\n<pre><code class=\"language-bash\">dotnet test<\/code><\/pre>\n<h3>Project\/Solution Options<\/h3>\n<p><div class=\"alert alert-info\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Breaking Change<\/strong><\/p>\nThe traditional syntax <strong>dotnet test \\&lt;path&gt;<\/strong> (e.g., <strong>dotnet test MyProject.csproj<\/strong>, <strong>dotnet test MySolution.sln<\/strong>, or <strong>dotnet test MyFolder<\/strong>) is no longer supported.\nYou must now use explicit options to specify your target.\n<\/div><\/p>\n<p>With MTP integration, you must use specific options to target projects, solutions, or directories. This change provides more deterministic behavior and clearer command semantics.<\/p>\n<p>To specify the path of the project file to run (folder name or full path). If not specified, it defaults to the current directory.<\/p>\n<pre><code class=\"language-bash\">dotnet test --project .\/TestProject\/TestProject.csproj<\/code><\/pre>\n<p>To specify the path of the solution file to run (folder name or full path). If not specified, it defaults to the current directory.<\/p>\n<pre><code class=\"language-bash\">dotnet test --solution .\/TestProjects\/TestProjects.sln<\/code><\/pre>\n<h3>Globbing Filter Option<\/h3>\n<p>The <code>--test-modules<\/code> option provides powerful globbing pattern support for targeting specific test assemblies. This is particularly useful when you need to run tests from pre-built assemblies or when working with complex solution structures where you want precise control over which test modules execute.<\/p>\n<p>To run tests for specific assemblies<\/p>\n<pre><code class=\"language-bash\">dotnet test --test-modules \"**\/bin\/**\/Debug\/net10.0\/TestProject.dll\"<\/code><\/pre>\n<p>To run tests for assemblies with a specific root directory<\/p>\n<pre><code class=\"language-bash\">dotnet test --test-modules \"**\/bin\/**\/Debug\/net10.0\/TestProject.dll\" --root-directory \"c:\\code\"<\/code><\/pre>\n<p>This approach is ideal for scenarios where you have already compiled test assemblies and want to execute them directly without going through the build process, providing the same flexibility locally that was previously only available through Azure DevOps task.<\/p>\n<p><div class=\"alert alert-info\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Mutually Exclusive Options<\/strong><\/p>\nThese target selection options are mutually exclusive &#8211; use only one at a time:<strong>&#8211;project<\/strong>, <strong>&#8211;solution<\/strong>, or <strong>&#8211;test-modules<\/strong>.<\/p>\n<p>When using <strong>&#8211;test-modules<\/strong>, build options (like <strong>&#8211;configuration<\/strong> or <strong>&#8211;framework<\/strong>) are not available since they don&#8217;t apply to pre-built assemblies.\n<\/div><\/p>\n<h3>Max Parallel Test Modules Option<\/h3>\n<p>It specifies the maximum number of test modules that can run in parallel.<\/p>\n<pre><code class=\"language-bash\">dotnet test --max-parallel-test-modules 4<\/code><\/pre>\n<h3>Output Options<\/h3>\n<p>We use the ANSI terminal, so when running <code>dotnet test<\/code>, the progress will be displayed as follows<\/p>\n<pre><code class=\"language-bash\">dotnet test<\/code><\/pre>\n<p><div style=\"width: 640px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-57713-1\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test.mp4?_=1\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test.mp4<\/a><\/video><\/div><\/p>\n<p>To disable outputting ANSI escape characters to screen.<\/p>\n<pre><code class=\"language-bash\">dotnet test --no-ansi<\/code><\/pre>\n<p><div style=\"width: 640px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-57713-2\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-no-ansi.mp4?_=2\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-no-ansi.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-no-ansi.mp4<\/a><\/video><\/div><\/p>\n<p>To disable reporting progress to screen.<\/p>\n<pre><code class=\"language-bash\">dotnet test --no-progress<\/code><\/pre>\n<p><div style=\"width: 640px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-57713-3\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-no-progress.mp4?_=3\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-no-progress.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-no-progress.mp4<\/a><\/video><\/div><\/p>\n<p>To specify the output verbosity when reporting tests. Valid values are <code>Normal<\/code> and <code>Detailed<\/code>. The default is <code>Normal<\/code>.<\/p>\n<pre><code class=\"language-bash\">dotnet test --output Detailed<\/code><\/pre>\n<p><div style=\"width: 640px;\" class=\"wp-video\"><video class=\"wp-video-shortcode\" id=\"video-57713-4\" width=\"640\" height=\"360\" preload=\"metadata\" controls=\"controls\"><source type=\"video\/mp4\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-output-detailed.mp4?_=4\" \/><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-output-detailed.mp4\">https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2025\/08\/dotnet-test-output-detailed.mp4<\/a><\/video><\/div><\/p>\n<h3>Test Application Arguments<\/h3>\n<p>Those extra arguments are passed to the test application(s). Use a space to separate multiple arguments. For more information and examples on what to pass, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/microsoft-testing-platform-intro\">Microsoft.Testing.Platform<\/a> overview and <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/testing\/microsoft-testing-platform-extensions\">Microsoft.Testing.Platform<\/a> extensions.<\/p>\n<h3>Build and Configuration Options<\/h3>\n<p>The following traditional <code>dotnet test<\/code> options remain available with the new version:<\/p>\n<pre><code class=\"language-bash\">dotnet test [-a|--arch &lt;ARCHITECTURE&gt;]\n            [-c|--configuration &lt;CONFIGURATION&gt;]\n            [-f|--framework &lt;FRAMEWORK&gt;]\n            [--os &lt;OS&gt;]\n            [-r|--runtime &lt;RUNTIME_IDENTIFIER&gt;]\n            [-v|--verbosity &lt;LEVEL&gt;]\n            [--no-build]\n            [--no-restore]<\/code><\/pre>\n<p>These options provide the same build and runtime control as before:<\/p>\n<ul>\n<li><strong>Architecture<\/strong>: Specify target architecture (<code>-a|--arch<\/code>)<\/li>\n<li><strong>Configuration<\/strong>: Set build configuration like Debug or Release (<code>-c|--configuration<\/code>)<\/li>\n<li><strong>Framework<\/strong>: Target specific framework version (<code>-f|--framework<\/code>)<\/li>\n<li><strong>Operating System<\/strong>: Target specific OS (<code>--os<\/code>)<\/li>\n<li><strong>Runtime<\/strong>: Specify runtime identifier (<code>-r|--runtime<\/code>)<\/li>\n<li><strong>Verbosity<\/strong>: Control output detail level (<code>-v|--verbosity<\/code>)<\/li>\n<li><strong>Build Control<\/strong>: Skip build (<code>--no-build<\/code>) or restore (<code>--no-restore<\/code>) steps<\/li>\n<\/ul>\n<p><div class=\"alert alert-info\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Info\"><\/i><strong>Incompatible Solution Options<\/strong><\/p>\nRunning tests for a solution with a global <code>RuntimeIdentifier<\/code> property (explicitly or via <code>--arch<\/code>, <code>--runtime<\/code>, or <code>--os<\/code>) is not supported. You can set <code>RuntimeIdentifier<\/code> on individual project level instead.\n<\/div><\/p>\n<h3>MSBuild Properties<\/h3>\n<p>MSBuild property support continues to work as before with MTP integration. You can pass one or more MSBuild properties using both short and long forms<\/p>\n<pre><code class=\"language-bash\"># Using short form\ndotnet test -p:DefineConstants=\"DEV\"\n\n# Using long form  \ndotnet test --property:Configuration=Release\n\n# Multiple properties\ndotnet test -p:DefineConstants=\"DEV\" -p:Configuration=Release<\/code><\/pre>\n<p>The same applies for \/property:property=value and its short form is \/p.<\/p>\n<h2>Wrapping up<\/h2>\n<p>The evolution of <code>dotnet test<\/code> with Microsoft.Testing.Platform represents a significant step forward in .NET testing capabilities. By moving from VSTest integration approach in .NET 9 to the MTP integration in .NET 10, developers now enjoy:<\/p>\n<ul>\n<li><strong>Simplified configuration<\/strong> through <code>global.json<\/code> file<\/li>\n<li><strong>Enhanced performance<\/strong>: more efficient communication between test applications and .NET CLI<\/li>\n<li><strong>Rich diagnostics<\/strong> with structured logging and ANSI-formatted output<\/li>\n<li><strong>Flexible test selection<\/strong> using advanced globbing patterns<\/li>\n<li><strong>Future-ready architecture<\/strong> designed for long-term .NET evolution<\/li>\n<\/ul>\n<p><div class=\"alert alert-success\"><p class=\"alert-divider\"><i class=\"fabric-icon fabric-icon--Lightbulb\"><\/i><strong>Get Started Today<\/strong><\/p>\nReady to upgrade? Start by converting a single test project to MTP, then gradually migrate your entire solution. The improved performance and diagnostics will immediately enhance your development workflow.\n<\/div><\/p>\n<h2>Community Feedback<\/h2>\n<p>For a detailed documentation on the new <code>dotnet test<\/code>, see <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/tools\/dotnet-test?tabs=dotnet-test-with-mtp\"><code>dotnet test<\/code><\/a>.<\/p>\n<p>We encourage you to try out these new capabilities and share your experience with the community. Your feedback helps us continue improving the .NET testing ecosystem. Please share your thoughts, report issues, or start discussions on the <a href=\"https:\/\/github.com\/dotnet\/sdk\">.NET SDK repository<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn how .NET 10 transforms dotnet test with native Microsoft.Testing.Platform integration, delivering better performance and enhanced diagnostics.<\/p>\n","protected":false},"author":144714,"featured_media":58520,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,756,636,7199],"tags":[136],"class_list":["post-57713","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-csharp","category-fsharp","category-visual-basic","tag-testing"],"acf":[],"blog_post_summary":"<p>Learn how .NET 10 transforms dotnet test with native Microsoft.Testing.Platform integration, delivering better performance and enhanced diagnostics.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/57713","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/144714"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=57713"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/57713\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58520"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=57713"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=57713"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=57713"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}