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 + + + + + + +