diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json
index aeb46d7b25..19a4712c00 100644
--- a/.claude-plugin/marketplace.json
+++ b/.claude-plugin/marketplace.json
@@ -38,6 +38,11 @@
"name": "dotnet-ai",
"source": "./plugins/dotnet-ai",
"description": "AI and ML skills for .NET: technology selection, LLM integration, agentic workflows, RAG pipelines, MCP, and classic ML with ML.NET."
+ },
+ {
+ "name": "dotnet-test",
+ "source": "./plugins/dotnet-test",
+ "description": "Skills for running, diagnosing, and migrating .NET tests: test execution, filtering, platform detection, and MSTest workflows."
}
]
}
diff --git a/.github/plugin/marketplace.json b/.github/plugin/marketplace.json
index aeb46d7b25..19a4712c00 100644
--- a/.github/plugin/marketplace.json
+++ b/.github/plugin/marketplace.json
@@ -38,6 +38,11 @@
"name": "dotnet-ai",
"source": "./plugins/dotnet-ai",
"description": "AI and ML skills for .NET: technology selection, LLM integration, agentic workflows, RAG pipelines, MCP, and classic ML with ML.NET."
+ },
+ {
+ "name": "dotnet-test",
+ "source": "./plugins/dotnet-test",
+ "description": "Skills for running, diagnosing, and migrating .NET tests: test execution, filtering, platform detection, and MSTest workflows."
}
]
}
diff --git a/README.md b/README.md
index 4cd2741d6f..239c08b570 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ This repository contains the .NET team's curated set of core skills and custom a
| [dotnet-upgrade](plugins/dotnet-upgrade/) | Skills for migrating and upgrading .NET projects across framework versions, language features, and compatibility targets. |
| [dotnet-maui](plugins/dotnet-maui/) | Skills for .NET MAUI development: environment setup, diagnostics, and troubleshooting. |
| [dotnet-ai](plugins/dotnet-ai/) | AI and ML skills for .NET: technology selection, LLM integration, agentic workflows, RAG pipelines, MCP, and classic ML with ML.NET. |
+| [dotnet-test](plugins/dotnet-test/) | Skills for running, diagnosing, and migrating .NET tests: test execution, filtering, platform detection, and MSTest workflows. |
## Installation
diff --git a/plugins/dotnet-test/plugin.json b/plugins/dotnet-test/plugin.json
index bd47c0eabb..e270b4eea3 100644
--- a/plugins/dotnet-test/plugin.json
+++ b/plugins/dotnet-test/plugin.json
@@ -1,6 +1,6 @@
{
"name": "dotnet-test",
"version": "0.1.0",
- "description": "Skills for running and diagnosing .NET tests: test execution, filtering, platform detection, and MSTest workflows.",
+ "description": "Skills for running, diagnosing, and migrating .NET tests: test execution, filtering, platform detection, and MSTest workflows.",
"skills": "./skills/"
}
diff --git a/plugins/dotnet-test/skills/migrate-vstest-to-mtp/SKILL.md b/plugins/dotnet-test/skills/migrate-vstest-to-mtp/SKILL.md
new file mode 100644
index 0000000000..fcbe43591d
--- /dev/null
+++ b/plugins/dotnet-test/skills/migrate-vstest-to-mtp/SKILL.md
@@ -0,0 +1,399 @@
+---
+name: migrate-vstest-to-mtp
+description: >
+ Migrate .NET test projects from VSTest to Microsoft.Testing.Platform (MTP).
+ USE FOR: enabling MTP runners (EnableMSTestRunner, EnableNUnitRunner,
+ UseMicrosoftTestingPlatformRunner), adding OutputType Exe, removing UseVSTest
+ from MSTest.Sdk, upgrading xunit v2 to xunit.v3, upgrading NUnit3TestAdapter
+ to 5.0.0+, translating CLI args (--logger to --report-trx, --collect to
+ --coverage, --blame-crash to --crashdump), migrating xUnit.net --filter to
+ --filter-class/--filter-trait/--filter-query, configuring global.json MTP mode
+ (.NET 10+) or TestingPlatformDotnetTestSupport (.NET 9-), replacing VSTest@3
+ with DotNetCoreCLI@2, installing MTP extension packages (TrxReport, CrashDump,
+ HangDump, CodeCoverage), fixing exit code 8 (--ignore-exit-code 8,
+ TESTINGPLATFORM_EXITCODE_IGNORE). Set properties in Directory.Build.props.
+ DO NOT USE FOR: framework migration (MSTest↔xUnit/NUnit), MSTest version
+ upgrades (use migrate-mstest-* skills), TFM upgrades, UWP/WinUI adapters.
+---
+
+# VSTest → Microsoft.Testing.Platform Migration
+
+Migrate a .NET test solution from VSTest to Microsoft.Testing.Platform (MTP). MTP is an executable-first test platform that supports Native AOT, trimming, `dotnet run`, `dotnet watch`, and direct executable execution. The outcome is a solution where all test projects run on MTP, `dotnet test` works correctly, and CI/CD pipelines are updated.
+
+> **Important**: Do not mix VSTest-based and MTP-based .NET test projects in the same solution or run configuration — this is an unsupported scenario.
+
+## When to Use
+
+- Switching from VSTest to Microsoft.Testing.Platform for any supported test framework
+- Enabling `dotnet run` / `dotnet watch` / direct executable execution for test projects
+- Enabling Native AOT or trimmed test execution
+- Replacing `vstest.console.exe` with `dotnet test` on MTP
+- Updating CI/CD pipelines from the VSTest task to the .NET Core CLI task
+- Updating `dotnet test` arguments from VSTest syntax to MTP syntax
+
+## When Not to Use
+
+- The project already runs on Microsoft.Testing.Platform — migration is done
+- Migrating between test frameworks (e.g., MSTest to xUnit.net) — different effort entirely
+- The project builds UWP or packaged WinUI test projects — MTP does not support these yet
+- The solution mixes .NET and non-.NET test adapters (e.g., JavaScript or C++ adapters) — VSTest is required
+- Upgrading MSTest versions — use `migrate-mstest-v1v2-to-v3` or `migrate-mstest-v3-to-v4`
+
+## Inputs
+
+| Input | Required | Description |
+|-------|----------|-------------|
+| Project or solution path | Yes | The `.csproj`, `.sln`, or `.slnx` entry point containing test projects |
+| Test framework | Yes | MSTest, NUnit, or xUnit.net. Auto-detect from package references if not provided |
+| .NET SDK version | No | The SDK version used to build. Determines `dotnet test` integration mode. Auto-detect from `global.json` or `dotnet --version` |
+| CI system | No | Azure DevOps, GitHub Actions, or other. Determines pipeline update guidance |
+
+## Workflow
+
+### Step 1: Assess the solution
+
+1. Identify the test framework for each test project:
+ - **MSTest**: References `MSTest`, `MSTest.TestFramework`, or uses `MSTest.Sdk`
+ - **NUnit**: References `NUnit` and `NUnit3TestAdapter`
+ - **xUnit.net**: References `xunit` (v2) or `xunit.v3` (v3)
+2. Check the .NET SDK version (`dotnet --version`) — this determines how `dotnet test` integrates with MTP
+3. Check whether a `Directory.Build.props` file exists at the solution or repo root — all MTP properties should go there for consistency
+4. Check for `vstest.console.exe` usage in CI scripts or pipeline definitions
+5. Check for VSTest-specific `dotnet test` arguments in CI scripts: `--filter`, `--logger`, `--collect`, `--settings`, `--blame*`
+6. Run `dotnet test` to establish a baseline of test pass/fail counts
+
+### Step 2: Set up Directory.Build.props
+
+> **Critical**: Always set MTP properties in `Directory.Build.props` at the solution or repo root — never per-project. This prevents inconsistent configuration where some projects use VSTest and others use MTP (an unsupported scenario).
+
+Create or update `Directory.Build.props` with **all** required MTP properties up front. The exact properties depend on the framework (see Step 3), but `OutputType Exe` is always required:
+
+```xml
+
+
+
+ Exe
+
+
+
+
+```
+
+If `Directory.Build.props` is shared with non-test projects, scope it with a condition:
+
+```xml
+
+ Exe
+
+```
+
+> **Note**: xUnit.net v3 and MSTest.Sdk already set `OutputType Exe` automatically. MSTest.Sdk also sets `EnableMSTestRunner` automatically. When using these, the properties are already handled — but including them in `Directory.Build.props` is harmless and keeps configuration explicit.
+
+### Step 3: Enable the framework-specific MTP runner
+
+Each framework has its own opt-in property. Add these in `Directory.Build.props` for consistency.
+
+#### MSTest
+
+**Option A — MSTest NuGet packages (3.2.0+):**
+
+```xml
+
+ true
+
+```
+
+Ensure the project references MSTest 3.2.0 or later. Recommend updating to the latest version.
+
+**Option B — MSTest.Sdk:**
+
+When using `MSTest.Sdk`, MTP is enabled by default — no `EnableMSTestRunner` or `OutputType Exe` property is needed (the SDK sets both automatically). The only action is: if the project has `true`, **remove it**. That property is what opts out of MTP.
+
+#### NUnit
+
+Requires `NUnit3TestAdapter` **5.0.0** or later.
+
+1. Update `NUnit3TestAdapter` to 5.0.0+:
+
+```xml
+
+```
+
+2. Enable the NUnit runner:
+
+```xml
+
+ true
+
+```
+
+#### xUnit.net
+
+Requires **xunit.v3** (v3 or later). If the project still uses xunit v2, it must be upgraded to v3 first.
+
+1. Replace xunit v2 packages with xunit.v3:
+
+```xml
+
+
+
+
+
+
+```
+
+2. Enable the MTP runner:
+
+```xml
+
+ true
+
+```
+
+> **Tip**: To preserve VSTest compatibility during a transition period, keep `xunit.runner.visualstudio` and `Microsoft.NET.Test.Sdk` alongside `xunit.v3`. Remove them once the migration is complete.
+
+### Step 4: Configure dotnet test integration
+
+The `dotnet test` integration depends on the .NET SDK version.
+
+#### .NET 10 SDK and later (recommended)
+
+Use the native MTP mode by adding a `test` section to `global.json`:
+
+```json
+{
+ "sdk": {
+ "version": "10.0.100"
+ },
+ "test": {
+ "runner": "Microsoft.Testing.Platform"
+ }
+}
+```
+
+In this mode, `dotnet test` arguments are passed directly — no extra `--` separator is needed.
+
+#### .NET 9 SDK and earlier
+
+Use the VSTest-bridge mode by adding this property in `Directory.Build.props`:
+
+```xml
+
+ true
+
+```
+
+> **Important**: In this mode, you must use `--` to separate `dotnet test` build arguments from MTP arguments. For example: `dotnet test --no-build -- --list-tests`.
+
+### Step 5: Update dotnet test command-line arguments
+
+VSTest-specific arguments must be translated to MTP equivalents. Build-related arguments (`-c`, `-f`, `--no-build`, `--nologo`, `-v`, etc.) are unchanged.
+
+| VSTest argument | MTP equivalent | Notes |
+|-----------------|----------------|-------|
+| `--test-adapter-path` | Not applicable | MTP does not use external adapter discovery |
+| `--blame` | Not applicable | |
+| `--blame-crash` | `--crashdump` | Requires `Microsoft.Testing.Extensions.CrashDump` NuGet package |
+| `--blame-crash-dump-type ` | `--crashdump-type ` | Requires CrashDump extension |
+| `--blame-hang` | `--hangdump` | Requires `Microsoft.Testing.Extensions.HangDump` NuGet package |
+| `--blame-hang-dump-type ` | `--hangdump-type ` | Requires HangDump extension |
+| `--blame-hang-timeout ` | `--hangdump-timeout ` | Requires HangDump extension |
+| `--collect "Code Coverage;Format=cobertura"` | `--coverage --coverage-output-format cobertura` | Per-extension arguments |
+| `-d\|--diag ` | `--diagnostic` | |
+| `--filter ` | Framework-dependent (see below) | |
+| `-l\|--logger trx` | `--report-trx` | Requires `Microsoft.Testing.Extensions.TrxReport` NuGet package |
+| `--results-directory ` | `--results-directory ` | Same |
+| `-s\|--settings ` | `--settings ` | MSTest and NUnit still support `.runsettings`. MTP also supports `testconfig.json` |
+| `-t\|--list-tests` | `--list-tests` | Same |
+| `-- ` | `--test-parameter` | Provided by VSTestBridge |
+
+#### Filter migration (important for xUnit.net)
+
+**MSTest and NUnit**: The `--filter` syntax is identical on both VSTest and MTP. No changes needed.
+
+**xUnit.net (breaking change)**: The VSTest `--filter` syntax is **not supported** on MTP. You must migrate to xUnit.net v3 native filter options. If your CI uses `--filter`, this is a required change.
+
+##### Simple filter translations
+
+| VSTest filter | xUnit.net MTP filter |
+|---------------|----------------------|
+| `--filter "FullyQualifiedName~MyClass"` | `--filter-class MyNamespace.MyClass` |
+| `--filter "FullyQualifiedName~MyMethod"` | `--filter-method MyMethod` |
+| `--filter "Category=Integration"` | `--filter-trait "Category=Integration"` |
+| `--filter "FullyQualifiedName!~Slow"` | `--filter-not-method Slow` |
+
+Multiple `--filter-*` options can be combined on the same command line. They are ANDed together:
+
+```shell
+dotnet test -- --filter-class MyNamespace.IntegrationTests --filter-trait "Category=Smoke"
+```
+
+##### Compound expressions with --filter-query
+
+For compound VSTest filter expressions (using `&`, `|`, `!`), use `--filter-query` which supports the [xUnit.net v3 query filter language](https://xunit.net/docs/query-filter-language). The syntax is segment-based:
+
+```
+////
+```
+
+Traits use bracket notation: `[name=value]` or `[name!=value]`. Combine expressions within a segment using parentheses with `&` (AND) or `|` (OR). Use `*` as a wildcard at the start or end of any segment.
+
+| VSTest compound filter | xUnit.net MTP --filter-query |
+|------------------------|------------------------------|
+| `--filter "FullyQualifiedName~IntegrationTests&Category=Smoke"` | `--filter-query "/*/*/IntegrationTests*/*[Category=Smoke]"` |
+| `--filter "Category=Unit\|Category=Integration"` | `--filter-query "/[(Category=Unit)\|(Category=Integration)]"` |
+| `--filter "FullyQualifiedName~Tests&FullyQualifiedName!~Slow"` | `--filter-query "/*/*/(*Tests*)&(!*Slow*)"` |
+| `--filter "FullyQualifiedName~MyMethod"` | `--filter-query "/*/*/*/MyMethod*"` |
+
+> **Reference**: See the [xUnit.net v3 query filter language documentation](https://xunit.net/docs/query-filter-language) for the full specification, including escaping special characters and negation.
+
+### Step 6: Install MTP extension packages (if needed)
+
+If CI scripts use TRX reporting, crash dumps, or hang dumps, add the corresponding NuGet packages:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Step 7: Update CI/CD pipelines
+
+#### Azure DevOps
+
+**If using the VSTest task (`VSTest@3`)**: Replace with the .NET Core CLI task (`DotNetCoreCLI@2`):
+
+```yaml
+# Before (VSTest task)
+- task: VSTest@3
+ inputs:
+ testAssemblyVer2: '**/*Tests.dll'
+ runSettingsFile: 'test.runsettings'
+
+# After (.NET Core CLI task)
+- task: DotNetCoreCLI@2
+ displayName: Run tests
+ inputs:
+ command: 'test'
+ arguments: '--no-build --configuration Release'
+```
+
+**If using DotNetCoreCLI@2 without .NET 10 native MTP**: Add `--` before MTP arguments:
+
+```yaml
+- task: DotNetCoreCLI@2
+ displayName: Run tests
+ inputs:
+ command: 'test'
+ arguments: '--no-build -- --report-trx --results-directory $(Agent.TempDirectory)'
+```
+
+**If using DotNetCoreCLI@2 with .NET 10 native MTP mode** (global.json configured): Pass MTP arguments directly:
+
+```yaml
+- task: DotNetCoreCLI@2
+ displayName: Run tests
+ inputs:
+ command: 'test'
+ arguments: '--no-build --report-trx --results-directory $(Agent.TempDirectory)'
+```
+
+#### GitHub Actions
+
+Update `dotnet test` invocations in workflow files with the same argument translations from Step 5.
+
+#### Replace vstest.console.exe
+
+If any script invokes `vstest.console.exe` directly, replace it with `dotnet test`. The test projects are now executables and can also be run directly.
+
+### Step 8: Handle behavioral differences
+
+#### Zero tests exit code
+
+VSTest silently succeeds when zero tests are discovered. MTP fails with **exit code 8**. Options:
+
+- Pass `--ignore-exit-code 8` when running tests
+- Add to `Directory.Build.props`:
+
+```xml
+
+ $(TestingPlatformCommandLineArguments) --ignore-exit-code 8
+
+```
+
+- Use environment variable: `TESTINGPLATFORM_EXITCODE_IGNORE=8`
+
+#### Console.InputEncoding preservation
+
+MTP always preserves the console codepage. If CI sets `chcp 65001` (UTF8) and tests start child processes with `CreateNoWindow = true`, those child processes may receive a UTF8 BOM that VSTest previously did not pass. Workaround:
+
+```csharp
+Console.InputEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
+```
+
+### Step 9: Remove VSTest-only packages (optional)
+
+Once migration is complete and verified, remove packages that are only needed for VSTest:
+
+- `Microsoft.NET.Test.Sdk` — not needed for MTP (MSTest.Sdk v4 already omits it by default)
+- `xunit.runner.visualstudio` — only needed for VSTest discovery of xUnit.net
+- `NUnit3TestAdapter` VSTest-only features — the adapter is still needed but only for the MTP runner
+
+> **Note**: If you need to maintain VSTest compatibility during a transition period, keep these packages.
+
+### Step 10: Verify
+
+1. Run `dotnet build` — confirm zero errors
+2. Run `dotnet test` — confirm all tests pass
+3. Compare test pass/fail counts to the pre-migration baseline
+4. Run the test executable directly (e.g., `./bin/Debug/net8.0/MyTests.exe`) — confirm it works
+5. Verify CI pipeline produces the expected test result artifacts (TRX files, code coverage, crash dumps)
+6. Test that Test Explorer in Visual Studio (17.14+) or VS Code discovers and runs tests
+
+## Validation
+
+- [ ] All test projects have `Exe`
+- [ ] Framework-specific runner property is set (`EnableMSTestRunner`, `EnableNUnitRunner`, or `UseMicrosoftTestingPlatformRunner`)
+- [ ] `dotnet test` integration is configured (global.json for .NET 10+ or `TestingPlatformDotnetTestSupport` for .NET 9 and earlier)
+- [ ] All VSTest-specific CLI arguments are translated to MTP equivalents
+- [ ] Required MTP extension NuGet packages are installed (TrxReport, CrashDump, HangDump, CodeCoverage as needed)
+- [ ] CI/CD pipeline tasks are updated (no VSTest@3 task, correct argument syntax)
+- [ ] Project builds with zero errors
+- [ ] All tests pass with `dotnet test`
+- [ ] No tests were lost during migration (compare test counts)
+- [ ] Test executable runs directly without `dotnet test`
+
+## Common Pitfalls
+
+| Pitfall | Solution |
+|---------|----------|
+| Mixing VSTest and MTP projects in the same solution | Migrate all test projects together — mixed mode is unsupported |
+| Missing `Exe` | Add to all test projects or `Directory.Build.props` |
+| `dotnet test` arguments ignored on .NET 9 and earlier | Use `--` to separate build args from MTP args: `dotnet test -- --report-trx` |
+| `--logger trx` produces no output | Replace with `--report-trx` and install `Microsoft.Testing.Extensions.TrxReport` |
+| `--collect "Code Coverage"` does nothing | Replace with `--coverage` and install `Microsoft.Testing.Extensions.CodeCoverage` |
+| `--filter` fails on xUnit.net v3 | VSTest `--filter` is not supported; use `--filter-class`, `--filter-method`, `--filter-trait` for simple filters, or `--filter-query` for compound expressions |
+| Exit code 8 on CI without failures | MTP fails when zero tests run; use `--ignore-exit-code 8` or fix test discovery |
+| VSTest task in Azure DevOps fails | Replace `VSTest@3` with `DotNetCoreCLI@2` task |
+| NUnit3TestAdapter < 5.0.0 | MTP requires adapter version 5.0.0 or later |
+| xUnit.net v2 does not support MTP | Upgrade to xunit.v3 first — MTP support is built into v3 |
+| Test Explorer not discovering tests | Update Visual Studio to 17.14+ or use VS Code with C# DevKit |
+| MSTest.Sdk v4 + vstest.console no longer works | MSTest.Sdk v4 no longer adds `Microsoft.NET.Test.Sdk` — add it explicitly or switch to `dotnet test` |
+| Properties set per-project instead of in Directory.Build.props | Centralize in `Directory.Build.props` to avoid inconsistent configuration |
+
+## More Info
+
+- [Test platforms overview](https://learn.microsoft.com/dotnet/core/testing/test-platforms-overview)
+- [Migrate from VSTest to Microsoft.Testing.Platform](https://learn.microsoft.com/dotnet/core/testing/migrating-vstest-microsoft-testing-platform)
+- [Microsoft.Testing.Platform overview](https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-intro)
+- [Testing with dotnet test](https://learn.microsoft.com/dotnet/core/testing/unit-testing-with-dotnet-test)
+- [Microsoft.Testing.Platform CLI options](https://learn.microsoft.com/dotnet/core/testing/microsoft-testing-platform-cli-options)
+- [Microsoft.Testing.Platform extensions](https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-extensions)
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/eval.yaml b/tests/dotnet-test/migrate-vstest-to-mtp/eval.yaml
new file mode 100644
index 0000000000..9e2959442e
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/eval.yaml
@@ -0,0 +1,373 @@
+scenarios:
+ # ============================================================================
+ # Goal 1: Migrate MSTest project from VSTest to MTP
+ # ============================================================================
+
+ - name: "Migrate MSTest project from VSTest to Microsoft.Testing.Platform"
+ prompt: |
+ I have a solution with 3 MSTest 3.8.0 test projects that currently run on VSTest.
+ I want to switch all of them to Microsoft.Testing.Platform. We're on .NET 8 SDK
+ and use `dotnet test --logger trx` in CI. What changes do I need to make to each
+ project and to the CI command? I want properties set consistently across all projects.
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/TestProject.csproj"
+ - path: "CalculatorTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/CalculatorTests.cs"
+ assertions:
+ - type: "output_matches"
+ pattern: "(EnableMSTestRunner|MSTest\\.Sdk)"
+ - type: "output_matches"
+ pattern: "(OutputType|Exe)"
+ - type: "output_matches"
+ pattern: "(Directory\\.Build\\.props|centralize|shared)"
+ rubric:
+ - "Adds Exe to Directory.Build.props (not per-project)"
+ - "Adds true to Directory.Build.props (not per-project)"
+ - "Configures dotnet test integration (TestingPlatformDotnetTestSupport in Directory.Build.props or global.json)"
+ - "Explains that --logger trx must change to --report-trx and requires Microsoft.Testing.Extensions.TrxReport package"
+ - "Mentions the -- separator requirement on .NET 8 for MTP arguments"
+ - "Does not suggest upgrading MSTest version (3.2.0+ is sufficient, 3.8.0 qualifies)"
+ timeout: 360
+
+ # ============================================================================
+ # Goal 2: Migrate NUnit project from VSTest to MTP
+ # ============================================================================
+
+ - name: "Migrate NUnit project from VSTest to Microsoft.Testing.Platform"
+ prompt: |
+ My NUnit test project uses NUnit3TestAdapter 4.6.0 and Microsoft.NET.Test.Sdk
+ on VSTest. I want to migrate to Microsoft.Testing.Platform. What do I need to change?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/TestProject.csproj"
+ - path: "CalculatorTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/CalculatorTests.cs"
+ assertions:
+ - type: "output_matches"
+ pattern: "(EnableNUnitRunner|NUnit.*runner)"
+ - type: "output_matches"
+ pattern: "(NUnit3TestAdapter|5\\.0\\.0|adapter.*5)"
+ - type: "output_matches"
+ pattern: "(OutputType|Exe)"
+ rubric:
+ - "Identifies that NUnit3TestAdapter must be upgraded to 5.0.0 or later for MTP support"
+ - "Adds true"
+ - "Adds Exe"
+ - "Mentions configuring dotnet test integration"
+ - "Recommends using Directory.Build.props for MTP properties"
+ timeout: 360
+
+ # ============================================================================
+ # Goal 3: Migrate xUnit.net project from VSTest to MTP
+ # ============================================================================
+
+ - name: "Migrate xUnit.net v2 project from VSTest to Microsoft.Testing.Platform"
+ prompt: |
+ My xUnit.net project uses xunit 2.9.3 with xunit.runner.visualstudio on VSTest.
+ I want to switch to Microsoft.Testing.Platform. What should I do?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/TestProject.csproj"
+ - path: "CalculatorTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/CalculatorTests.cs"
+ assertions:
+ - type: "output_matches"
+ pattern: "(xunit\\.v3|v3)"
+ - type: "output_matches"
+ pattern: "(UseMicrosoftTestingPlatformRunner|MTP.*runner)"
+ - type: "output_matches"
+ pattern: "(OutputType|Exe)"
+ rubric:
+ - "Identifies that xunit v2 does not support MTP and must be upgraded to xunit.v3"
+ - "Shows how to replace xunit 2.x and xunit.runner.visualstudio packages with xunit.v3"
+ - "Adds true"
+ - "Adds Exe"
+ - "Mentions that --filter syntax changes for xUnit.net on MTP (e.g., --filter-class, --filter-trait, --filter-query)"
+ timeout: 360
+
+ # ============================================================================
+ # Goal 4: Update Azure DevOps CI pipeline for MTP
+ # ============================================================================
+
+ - name: "Update Azure DevOps pipeline from VSTest task to MTP"
+ prompt: |
+ Our CI pipeline uses the VSTest@3 task to run MSTest tests. We're migrating
+ to Microsoft.Testing.Platform. We also produce TRX reports and collect code
+ coverage. How do we update the pipeline?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/TestProject.csproj"
+ - path: "ServiceTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/ServiceTests.cs"
+ - path: "azure-pipelines.yml"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/azure-pipelines.yml"
+ assertions:
+ - type: "output_matches"
+ pattern: "(DotNetCoreCLI|dotnet test)"
+ - type: "output_matches"
+ pattern: "(VSTest@3|replace|remove)"
+ - type: "output_matches"
+ pattern: "(report-trx|TrxReport)"
+ rubric:
+ - "Identifies that VSTest@3 task must be replaced with DotNetCoreCLI@2 task"
+ - "Shows the updated pipeline YAML using DotNetCoreCLI@2 with command 'test'"
+ - "Translates --logger trx to --report-trx and mentions Microsoft.Testing.Extensions.TrxReport package"
+ - "Translates code coverage collection to --coverage and mentions CodeCoverage extension package"
+ - "Explains the -- separator needed for MTP arguments on .NET 9 and earlier"
+ timeout: 300
+
+ # ============================================================================
+ # Goal 5: MSTest.Sdk project with UseVSTest
+ # ============================================================================
+
+ - name: "Migrate MSTest.Sdk project that explicitly uses VSTest"
+ prompt: |
+ My project uses MSTest.Sdk/3.8.0 with true. I want
+ to switch to Microsoft.Testing.Platform. What do I need to change?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/TestProject.csproj"
+ - path: "AppTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/AppTests.cs"
+ assertions:
+ - type: "output_matches"
+ pattern: "(UseVSTest|remove|delete)"
+ - type: "output_matches"
+ pattern: "(MSTest\\.Sdk|MTP.*default)"
+ rubric:
+ - "Identifies that MSTest.Sdk uses MTP by default and UseVSTest opts out of it"
+ - "Recommends removing true to enable MTP"
+ - "Notes that MSTest.Sdk automatically sets EnableMSTestRunner and OutputType Exe — no additional properties needed"
+ - "Mentions configuring dotnet test integration (TestingPlatformDotnetTestSupport or global.json)"
+ timeout: 360
+
+ # ============================================================================
+ # Goal 6: Translate dotnet test arguments
+ # ============================================================================
+
+ - name: "Translate dotnet test VSTest arguments to MTP equivalents"
+ prompt: |
+ Our CI runs `dotnet test --filter "Category=Integration" --logger trx --collect "Code Coverage;Format=cobertura" --blame-crash --blame-hang --blame-hang-timeout 5m`.
+ We're switching to Microsoft.Testing.Platform on .NET 8. What should the
+ new command look like?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/TestProject.csproj"
+ - path: "CalculatorTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/CalculatorTests.cs"
+ assertions:
+ - type: "output_matches"
+ pattern: "(report-trx|--report-trx)"
+ - type: "output_matches"
+ pattern: "(--coverage|CodeCoverage)"
+ - type: "output_matches"
+ pattern: "(crashdump|--crashdump)"
+ - type: "output_matches"
+ pattern: "(hangdump|--hangdump)"
+ rubric:
+ - "Translates --logger trx to --report-trx"
+ - "Translates --collect \"Code Coverage;Format=cobertura\" to --coverage --coverage-output-format cobertura"
+ - "Translates --blame-crash to --crashdump"
+ - "Translates --blame-hang to --hangdump"
+ - "Translates --blame-hang-timeout 5m to --hangdump-timeout 5m"
+ - "Mentions the -- separator needed on .NET 8 (pre-.NET 10)"
+ - "Lists the required NuGet extension packages (TrxReport, CrashDump, HangDump, CodeCoverage)"
+ timeout: 300
+
+ # ============================================================================
+ # Goal 7: Handle zero-test exit code behavioral change
+ # ============================================================================
+
+ - name: "Handle exit code 8 when migrating from VSTest to MTP"
+ prompt: |
+ We migrated our test project from VSTest to Microsoft.Testing.Platform.
+ Now our CI pipeline fails with exit code 8 even though no tests actually
+ failed. Some test assemblies have conditional compilation and produce zero
+ tests on certain configurations. With VSTest this was fine. How do I fix
+ this behavioral difference?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ content: |
+
+
+ net8.0
+ Exe
+ true
+ true
+
+
+
+
+
+ - path: "ConditionalTests.cs"
+ content: |
+ using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+ namespace MyApp.Tests;
+
+ #if INCLUDE_SLOW_TESTS
+ [TestClass]
+ public class SlowIntegrationTests
+ {
+ [TestMethod]
+ public void LongRunning_Succeeds()
+ {
+ Assert.IsTrue(true);
+ }
+ }
+ #endif
+ assertions:
+ - type: "output_matches"
+ pattern: "(exit.?code.?8|ignore-exit-code|zero.?test)"
+ rubric:
+ - "Explains that MTP fails with exit code 8 when zero tests are discovered (unlike VSTest which succeeds)"
+ - "Recommends --ignore-exit-code 8 as a command-line option"
+ - "Shows how to set it via TestingPlatformCommandLineArguments MSBuild property"
+ - "Mentions TESTINGPLATFORM_EXITCODE_IGNORE environment variable as an alternative"
+ timeout: 360
+
+ # ============================================================================
+ # Goal 8: Configure global.json for .NET 10 native MTP mode
+ # ============================================================================
+
+ - name: "Configure dotnet test MTP mode on .NET 10 SDK"
+ prompt: |
+ We're on .NET 10 SDK and migrating to Microsoft.Testing.Platform. I read
+ that .NET 10 has native MTP support in dotnet test. How do I set it up?
+ Do I still need TestingPlatformDotnetTestSupport?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ content: |
+
+
+ net10.0
+ Exe
+ true
+ true
+
+
+
+
+
+ - path: "global.json"
+ content: |
+ {
+ "sdk": {
+ "version": "10.0.100"
+ }
+ }
+ assertions:
+ - type: "output_matches"
+ pattern: "(global\\.json|test.*runner)"
+ - type: "output_matches"
+ pattern: "(Microsoft\\.Testing\\.Platform|MTP)"
+ rubric:
+ - "Shows how to add the test.runner section to global.json"
+ - "Explains that TestingPlatformDotnetTestSupport is no longer needed on .NET 10 with native MTP mode"
+ - "Explains that the -- separator is no longer needed in .NET 10 native MTP mode"
+ - "Recommends using the native MTP mode for .NET 10+ instead of the VSTest bridge"
+ timeout: 240
+
+ # ============================================================================
+ # Goal 9: xUnit.net filter migration
+ # ============================================================================
+
+ - name: "Migrate xUnit.net VSTest filter syntax to MTP"
+ prompt: |
+ My CI uses `dotnet test --filter "FullyQualifiedName~IntegrationTests&Category=Smoke"`
+ with xUnit.net v2. I'm upgrading to xunit.v3 with MTP. The --filter no longer
+ works the same way. How do I translate the filter?
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ content: |
+
+
+ net8.0
+ Exe
+ true
+
+
+
+
+
+ - path: "IntegrationTests.cs"
+ content: |
+ using Xunit;
+
+ namespace MyApp.Tests;
+
+ [Trait("Category", "Smoke")]
+ public class IntegrationTests
+ {
+ [Fact]
+ public void HealthEndpoint_ReturnsOk()
+ {
+ Assert.True(true);
+ }
+
+ [Theory]
+ [InlineData("api/v1")]
+ [InlineData("api/v2")]
+ public void ApiEndpoint_ReturnsOk(string path)
+ {
+ Assert.False(string.IsNullOrEmpty(path));
+ }
+ }
+ assertions:
+ - type: "output_matches"
+ pattern: "(filter-class|filter-trait|filter-method|filter-query)"
+ rubric:
+ - "Explains that VSTest --filter syntax is not supported for xUnit.net on MTP"
+ - "Translates FullyQualifiedName~IntegrationTests to --filter-class or --filter-query"
+ - "Translates Category=Smoke to --filter-trait \"Category=Smoke\""
+ - "Mentions --filter-query for complex filter expressions"
+ - "References xUnit.net query filter language documentation"
+ timeout: 360
+
+ # ============================================================================
+ # Goal 10: Full end-to-end migration with multiple projects
+ # ============================================================================
+
+ - name: "Full VSTest to MTP migration plan for MSTest solution"
+ prompt: |
+ I have a solution with 5 MSTest test projects all using VSTest (MSTest.TestFramework 3.8.0,
+ MSTest.TestAdapter 3.8.0, Microsoft.NET.Test.Sdk 17.13.0). We use Azure DevOps
+ with the VSTest@3 task, TRX reporting, and code coverage. We're on .NET 8 SDK.
+ I want a complete migration plan to Microsoft.Testing.Platform.
+ setup:
+ files:
+ - path: "TestProject.csproj"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/TestProject.csproj"
+ - path: "CalculatorTests.cs"
+ source: "../../../../tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/CalculatorTests.cs"
+ assertions:
+ - type: "output_matches"
+ pattern: "(EnableMSTestRunner|MSTest.*runner)"
+ - type: "output_matches"
+ pattern: "(OutputType|Exe)"
+ - type: "output_matches"
+ pattern: "(Directory\\.Build\\.props|centralize)"
+ - type: "output_matches"
+ pattern: "(DotNetCoreCLI|VSTest@3|pipeline)"
+ - type: "output_matches"
+ pattern: "(TrxReport|report-trx)"
+ rubric:
+ - "Recommends creating or updating Directory.Build.props with OutputType, EnableMSTestRunner, and TestingPlatformDotnetTestSupport"
+ - "Explains the properties should be set centrally, not per-project"
+ - "Covers replacing VSTest@3 task with DotNetCoreCLI@2"
+ - "Covers TRX reporting migration (--report-trx and TrxReport NuGet package)"
+ - "Covers code coverage migration (--coverage and CodeCoverage NuGet package)"
+ - "Mentions the -- separator requirement on .NET 8"
+ - "Includes verification steps (build, test, compare counts, check CI artifacts)"
+ - "Mentions possibility of exit code 8 for zero-test assemblies"
+ timeout: 300
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/ServiceTests.cs b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/ServiceTests.cs
new file mode 100644
index 0000000000..542a31e711
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/ServiceTests.cs
@@ -0,0 +1,19 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace MyApp.Tests;
+
+[TestClass]
+public class ServiceTests
+{
+ [TestMethod]
+ public void HealthCheck_ReturnsOk()
+ {
+ Assert.IsTrue(true);
+ }
+
+ [TestMethod]
+ public void GetStatus_ReturnsRunning()
+ {
+ Assert.AreEqual("Running", "Running");
+ }
+}
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/TestProject.csproj b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/TestProject.csproj
new file mode 100644
index 0000000000..8948a2db6b
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/TestProject.csproj
@@ -0,0 +1,13 @@
+
+
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/azure-pipelines.yml b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/azure-pipelines.yml
new file mode 100644
index 0000000000..c7089e55ee
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-ci-pipeline/azure-pipelines.yml
@@ -0,0 +1,36 @@
+trigger:
+ - main
+
+pool:
+ vmImage: 'ubuntu-latest'
+
+steps:
+ - task: UseDotNet@2
+ inputs:
+ packageType: 'sdk'
+ version: '8.0.x'
+
+ - task: DotNetCoreCLI@2
+ displayName: Restore
+ inputs:
+ command: 'restore'
+
+ - task: DotNetCoreCLI@2
+ displayName: Build
+ inputs:
+ command: 'build'
+ arguments: '--no-restore --configuration Release'
+
+ - task: VSTest@3
+ displayName: Run tests
+ inputs:
+ testAssemblyVer2: |
+ **/*Tests.dll
+ !**/obj/**
+ runSettingsFile: 'test.runsettings'
+ codeCoverageEnabled: true
+
+ - task: PublishTestResults@2
+ inputs:
+ testResultsFormat: 'VSTest'
+ testResultsFiles: '**/*.trx'
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/AppTests.cs b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/AppTests.cs
new file mode 100644
index 0000000000..d1ee2c6bb9
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/AppTests.cs
@@ -0,0 +1,19 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace MyApp.Tests;
+
+[TestClass]
+public class AppTests
+{
+ [TestMethod]
+ public void Initialize_Succeeds()
+ {
+ Assert.IsTrue(true);
+ }
+
+ [TestMethod]
+ public void Shutdown_Succeeds()
+ {
+ Assert.IsTrue(true);
+ }
+}
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/TestProject.csproj b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/TestProject.csproj
new file mode 100644
index 0000000000..9a3a2041b0
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-sdk-vstest/TestProject.csproj
@@ -0,0 +1,8 @@
+
+
+ net8.0
+ enable
+ enable
+ true
+
+
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/CalculatorTests.cs b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/CalculatorTests.cs
new file mode 100644
index 0000000000..f85911b61e
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/CalculatorTests.cs
@@ -0,0 +1,26 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace MyApp.Tests;
+
+[TestClass]
+public class CalculatorTests
+{
+ [TestMethod]
+ public void Add_ReturnsCorrectSum()
+ {
+ Assert.AreEqual(4, 2 + 2);
+ }
+
+ [TestMethod]
+ public void Subtract_ReturnsCorrectDifference()
+ {
+ Assert.AreEqual(0, 2 - 2);
+ }
+
+ [TestMethod]
+ [TestCategory("Slow")]
+ public void Multiply_ReturnsCorrectProduct()
+ {
+ Assert.AreEqual(6, 2 * 3);
+ }
+}
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/TestProject.csproj b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/TestProject.csproj
new file mode 100644
index 0000000000..8948a2db6b
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/mstest-vstest/TestProject.csproj
@@ -0,0 +1,13 @@
+
+
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/CalculatorTests.cs b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/CalculatorTests.cs
new file mode 100644
index 0000000000..a5afea22c8
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/CalculatorTests.cs
@@ -0,0 +1,26 @@
+using NUnit.Framework;
+
+namespace MyApp.Tests;
+
+[TestFixture]
+public class CalculatorTests
+{
+ [Test]
+ public void Add_ReturnsCorrectSum()
+ {
+ Assert.That(2 + 2, Is.EqualTo(4));
+ }
+
+ [Test]
+ public void Subtract_ReturnsCorrectDifference()
+ {
+ Assert.That(2 - 2, Is.EqualTo(0));
+ }
+
+ [Test]
+ [Category("Slow")]
+ public void Multiply_ReturnsCorrectProduct()
+ {
+ Assert.That(2 * 3, Is.EqualTo(6));
+ }
+}
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/TestProject.csproj b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/TestProject.csproj
new file mode 100644
index 0000000000..b40618c0ec
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/nunit-vstest/TestProject.csproj
@@ -0,0 +1,14 @@
+
+
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/CalculatorTests.cs b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/CalculatorTests.cs
new file mode 100644
index 0000000000..f735cde2ff
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/CalculatorTests.cs
@@ -0,0 +1,26 @@
+using Xunit;
+
+namespace MyApp.Tests;
+
+public class CalculatorTests
+{
+ [Fact]
+ public void Add_ReturnsCorrectSum()
+ {
+ Assert.Equal(4, 2 + 2);
+ }
+
+ [Fact]
+ public void Subtract_ReturnsCorrectDifference()
+ {
+ Assert.Equal(0, 2 - 2);
+ }
+
+ [Theory]
+ [InlineData(2, 3, 6)]
+ [InlineData(4, 5, 20)]
+ public void Multiply_ReturnsCorrectProduct(int a, int b, int expected)
+ {
+ Assert.Equal(expected, a * b);
+ }
+}
diff --git a/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/TestProject.csproj b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/TestProject.csproj
new file mode 100644
index 0000000000..3c97eca463
--- /dev/null
+++ b/tests/dotnet-test/migrate-vstest-to-mtp/fixtures/xunit-vstest/TestProject.csproj
@@ -0,0 +1,13 @@
+
+
+ net8.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+