Skip to main content

migrate-xunit-to-mstest

Migrate .NET test projects from xUnit.net (v2 or v3) to MSTest v4. USE FOR: convert/migrate xUnit tests to MSTest, replace xunit/xunit.v3 packages, port [Fact]/[Theory]/[InlineData]/[MemberData]/[ClassData] to [TestMethod]/[DataRow]/[DynamicData], port Assert.Equal/True/Throws/ThrowsAsync to Assert.AreEqual/IsTrue/ThrowsExactly/ThrowsExactlyAsync, port IClassFixture/ ICollectionFixture/IDisposable/IAsyncLifetime/ITestOutputHelper/[Trait]/[Fact(Skip)] to MSTest equivalents, preserve xUnit parallel-class default via [assembly: Parallelize(Scope = ClassLevel)], remove xunit.runner.json. DO NOT USE FOR: xUnit v2 -> v3 upgrade (use migrate-xunit-to-xunit-v3); MSTest -> xUnit, NUnit/TUnit -> MSTest (no skills exist); MSTest version upgrades (use migrate-mstest-v1v2-to-v3 or migrate-mstest-v3-to-v4); VSTest <-> MTP only (use migrate-vstest-to-mtp); general .NET upgrades.

Quellinformationen

Repository
dotnet/sdk
Letzte Quellaktivität
3. September 2026 um 11:14
Erkannte Sprache von SKILL.md
Englisch
Sterne
3.227
Forks
1.300

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
migrate-xunit-to-mstest
description
Migrate .NET test projects from xUnit.net (v2 or v3) to MSTest v4. USE FOR: convert/migrate xUnit tests to MSTest, replace xunit/xunit.v3 packages, port [Fact]/[Theory]/[InlineData]/[MemberData]/[ClassData] to [TestMethod]/[DataRow]/[DynamicData], port Assert.Equal/True/Throws/ThrowsAsync to Assert.AreEqual/IsTrue/ThrowsExactly/ThrowsExactlyAsync, port IClassFixture/ ICollectionFixture/IDisposable/IAsyncLifetime/ITestOutputHelper/[Trait]/[Fact(Skip)] to MSTest equivalents, preserve xUnit parallel-class default via [assembly: Parallelize(Scope = ClassLevel)], remove xunit.runner.json. DO NOT USE FOR: xUnit v2 -> v3 upgrade (use migrate-xunit-to-xunit-v3); MSTest -> xUnit, NUnit/TUnit -> MSTest (no skills exist); MSTest version upgrades (use migrate-mstest-v1v2-to-v3 or migrate-mstest-v3-to-v4); VSTest <-> MTP only (use migrate-vstest-to-mtp); general .NET upgrades.
license
MIT
# xUnit -> MSTest Migration Migrate a .NET test project from xUnit.net (v2 or v3) to MSTest v4. The outcome is a project that: - References MSTest v4 packages (or `MSTest.Sdk` 4.x) instead of `xunit*` / `xunit.v3.*` - Has every `[Fact]`/`[Theory]` rewritten as `[TestMethod]` and every assertion mapped to the MSTest equivalent - Builds cleanly with the same target framework - Passes the same set of tests (modulo intentional changes documented below) - Preserves the **current test platform** (VSTest stays on VSTest; MTP stays on MTP) This is a **cross-framework** migration. Do not bundle it with a version upgrade or a platform switch in the same pass -- if both are needed, do this skill first, commit, then run `migrate-mstest-v3-to-v4` (if you stopped on v3) or `migrate-vstest-to-mtp`. ## When to Use - The project references `xunit`, `xunit.assert`, `xunit.core`, `xunit.extensibility.core`/`execution`, `xunit.abstractions`, or any `xunit.v3.*` package, and you want to switch to MSTest - You want a single .NET test framework across a solution that today mixes xUnit and MSTest ## Inputs | Input | Required | Description | |-------|----------|-------------| | Project or solution path | Yes | The `.csproj`, `.sln`, or `.slnx` containing xUnit test projects | | Build command | No | How to build (e.g., `dotnet build`). Auto-detect if not provided | | Test command | No | How to run tests (e.g., `dotnet test`). Auto-detect if not provided | ## Response Guidelines - **Always identify the current xUnit version first.** State whether the project is on xUnit v2 (`xunit` 2.x) or xUnit v3 (`xunit.v3` / `xunit.v3.*`) before recommending changes. This grounds the migration advice -- some breaking-change steps only apply to one version. - **Always preserve the current test platform.** If the project runs on VSTest, keep VSTest. If it runs on MTP (e.g., xUnit v3 native MTP, or `<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>`), keep MTP. Recommend `migrate-vstest-to-mtp` as a separate follow-up only if the user asks for it. - **Explicitly communicate every judgement-call decision** before applying it -- otherwise the user cannot tell what changed semantically. In particular: - **Fixture scope changes** (Step 8): state the source scope (class / collection / assembly) and the target scope you chose, plus what gets shared and what gets serialized. A silent widening from collection to assembly is the most common way this migration regresses tests. - **Parallelization** (Step 11): state that **MSTest defaults to serial execution** (xUnit parallelizes classes by default), so an explicit `[assembly: Parallelize(...)]` is **required** to match xUnit's behaviour -- omitting it silently halves CI throughput. - **`Assert.Throws<T>` -> `Assert.ThrowsExactly<T>`** (Step 6): mention the exact-type-vs-any-derived semantic flip so reviewers know the assertion was deliberately renamed, not just translated. - **Specific API mapping questions** (assertions, fixtures, output helper, etc.): jump to the relevant step. Do not run the full workflow. - **Full migration requests**: follow the workflow end-to-end. - **Focused fix requests** (specific compile error after a partial migration): address only that error using the mapping reference. Do not walk the full workflow. - **Code samples**: show concrete before/after using the user's actual type/method names, not generic placeholders. ## Strategy The conversion is mechanical for ~80% of code (attributes and simple assertions) and judgement-based for ~20% (collection fixtures, custom data attributes, exact-type-vs-derived exception assertions, parallelization semantics). Always do the mechanical pass first so build errors point you at the judgement areas. ## Mapping Reference For the full attribute/assertion/fixture/lifecycle mapping tables -- including semantic traps (`Assert.Throws<T>` vs `Assert.ThrowsAny<T>`, `IClassFixture` vs `ICollectionFixture` scope), edge cases (`TheoryData<T...>`, `MemberType=`, custom `DataAttribute`, custom `FactAttribute`, `Record.Exception`), and copy-pasteable before/after snippets -- see [`references/mapping-cheatsheet.md`](references/mapping-cheatsheet.md). Load it whenever you need a specific xUnit -> MSTest equivalent. For writing idiomatic MSTest code (modern assertion APIs, lifecycle patterns, data-driven conventions, `Assert.HasCount`/`IsEmpty`/`StartsWith`, etc.), see the `writing-mstest-tests` skill. **Do not re-derive idiomatic MSTest patterns here.** Apply this skill to *convert*; apply `writing-mstest-tests` to *polish*. ## Workflow > **Commit strategy:** Commit after Step 2 (packages updated, builds broken), after Step 6 (attributes converted, asserts fixed), and after Step 8 (fixtures/lifecycle rewritten, tests pass). Commit before fixing follow-up cleanup so reviewers can bisect. ### Step 1: Assess the project 1. Locate every test project. Read `.csproj`, `Directory.Build.props`, `Directory.Packages.props`, and `global.json`. 2. Identify the **xUnit version**: - `xunit` 2.x (+ `xunit.assert` / `xunit.core` / `xunit.abstractions`) -> **xUnit v2** - `xunit.v3` / `xunit.v3.*` -> **xUnit v3** 3. Identify the **current test platform** (this dictates what to keep, not what to change) by invoking the `platform-detection` skill. The xUnit/MTP matrix is nuanced -- xunit.v3 inside Test Explorer is MTP by default unless opted out, while xunit.v3 inside `dotnet test` depends on the `xunit.v3.mtp-v*` packages -- so do not try to inline a shortcut here. Quick signals to feed into that skill: `xunit.runner.visualstudio` (v2) usually means VSTest; `xunit.v3.mtp-v*` / `xunit.v3.core.mtp-v*` packages or `YTest.MTP.XUnit2` (v2 MTP shim) usually mean MTP. `<UseMicrosoftTestingPlatformRunner>` only affects `dotnet run` and is **not** a reliable VSTest-vs-MTP signal on its own. 4. Verify the `TargetFramework` is supported by MSTest v4: - **Supported**: `net8.0`, `net9.0`, `net462`+, `netstandard2.0` (test library only), `uap10.0.16299`, `net8.0-windows10.0.18362.0` (WinUI), `net9.0-windows10.0.17763.0` (modern UWP). - **Unsupported**: .NET Core 3.1, `net5.0`-`net7.0`. **STOP** and ask the user to upgrade the TFM first, or migrate to MSTest v3 (then use `migrate-mstest-v3-to-v4` after a TFM bump). 5. Inventory high-risk patterns -- scan for these and flag them now so you can plan judgement steps later: - **Parallelization differences (Step 11)** -- xUnit parallelizes test classes by default; MSTest does not. This is the **single most common source of post-migration regressions**: tests that depended on isolation by parallel scheduling, on the lack of it, or on shared static state can pass differently. Decide the target parallelization model now -- do not leave it as the MSTest default by accident. - `ICollectionFixture<T>` / `[CollectionDefinition]` (scope concern -- see Step 8) - Custom `DataAttribute` / custom `FactAttribute` / custom `TheoryAttribute` subclasses (manual conversion to `ITestDataSource` / `TestMethodAttribute` -- see Step 5) - `Assert.Throws<T>` (xUnit semantics = exact type; maps to `Assert.ThrowsExactly<T>`, **not** `Assert.Throws<T>`) - `Record.Exception` / `Record.ExceptionAsync` (manual conversion) - `Assert.Raises*` / event assertions (no MSTest equivalent -- manual) - xUnit v3: `[assembly: CaptureConsole]` and other v3-only assembly attributes 6. **Inventory state shared between tests** -- static fields/properties, singletons, file paths, well-known ports, in-memory caches, database connection strings pointing at a single shared DB, environment variables. Whether parallelization is on or off, switching frameworks changes the *order* and *concurrency* in which these are touched. List them now so you can decide in Step 11 whether to enable parallelism, serialize specific classes with `[DoNotParallelize]`, or refactor the shared state. 7. Run a baseline build + test to record the current pass/fail count for parity check at Step 13. Re-run a second time -- if the xUnit run is **flaky** today, those flakes are almost certainly caused by parallel scheduling and will manifest differently after migration. Flag any flaky tests now. ### Step 2: Replace packages > Choose the package option that matches what the project uses today. **When the user says "preserve VSTest" -- or the existing project uses explicit `PackageReference`s -- default to Option A (`MSTest` metapackage).** Reach for Option B (`MSTest.Sdk`) only when the user explicitly asks to modernize the SDK or already uses `MSTest.Sdk` elsewhere in the solution; if you adopt it, you must preserve the platform from Step 1. **Remove** every xUnit package reference (from `.csproj`, `Directory.Build.props`, `Directory.Packages.props`): - `xunit`, `xunit.abstractions`, `xunit.assert`, `xunit.core` - `xunit.extensibility.core`, `xunit.extensibility.execution` - `xunit.runner.visualstudio` - `xunit.v3`, `xunit.v3.assert`, `xunit.v3.core`, `xunit.v3.extensibility.core` - `xunit.v3.mtp-v1`, `xunit.v3.mtp-v2`, `xunit.v3.core.mtp-v1`, `xunit.v3.core.mtp-v2` - `YTest.MTP.XUnit2` (xUnit v2 MTP shim) - Companion packages: `Xunit.SkippableFact`, `Xunit.Combinatorial`, `Xunit.StaFact` (see Step 10) **Add** MSTest v4. Two options -- both correct. **Option A -- `MSTest` metapackage (recommended for incremental migrations):** ```xml <ItemGroup> <PackageReference Include="MSTest" Version="4.1.0" /> </ItemGroup> ``` The `MSTest` metapackage pulls in `MSTest.TestFramework`, `MSTest.TestAdapter`, `MSTest.Analyzers`, and `Microsoft.NET.Test.Sdk` -- so VSTest discovery (`vstest.console`, classic `dotnet test`) still works. > **MTP code-coverage caveat for Option A:** `Microsoft.NET.Test.Sdk` pulls VSTest's `Microsoft.CodeCoverage` transitively. If the project from Step 1 is on **MTP** and uses code coverage, that transitive dependency can interfere with MTP's collector (`Microsoft.Testing.Extensions.CodeCoverage`). Prefer **Option B** (`MSTest.Sdk` without `UseVSTest`) for MTP projects -- the SDK omits `Microsoft.NET.Test.Sdk` and wires the MTP coverage collector instead. If you must stay on Option A for an MTP project, verify coverage works on a representative test run before merging. **Option B -- `MSTest.Sdk`:** ```xml <Project Sdk="MSTest.Sdk/4.1.0"> <PropertyGroup> <!-- Keep the project's existing TargetFramework -- do NOT retarget during migration. --> <TargetFramework>$(ExistingTargetFramework)</TargetFramework> </PropertyGroup> </Project> ``` `MSTest.Sdk` defaults to **MTP**. To preserve a VSTest project, opt back in with `<UseVSTest>true</UseVSTest>` -- the SDK then pulls in `Microsoft.NET.Test.Sdk` automatically (no extra `PackageReference` needed): ```xml <PropertyGroup> <UseVSTest>true</UseVSTest> </PropertyGroup> ``` For solutions with several test projects, prefer pinning the `MSTest.Sdk` version in `global.json` so it lives in one place: ```json { "msbuild-sdks": { "MSTest.Sdk": "4.1.0" } } ``` With the pin in `global.json`, the project line simplifies to `<Project Sdk="MSTest.Sdk">`. When switching to `MSTest.Sdk`, also remove now-redundant properties: `<OutputType>Exe</OutputType>`, `<IsPackable>false</IsPackable>`, `<IsTestProject>true</IsTestProject>`, `<EnableMSTestRunner>`. `MSTest.Sdk` also adds `Microsoft.VisualStudio.TestTools.UnitTesting` as an **implicit global using**. Do **not** add `<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />` to the project (it's noise) and skip the per-file `using Microsoft.VisualStudio.TestTools.UnitTesting;` in Step 4 -- you only need it for projects on Option A (the `MSTest` metapackage). ### Step 3: Update project configuration 1. **Preserve the runner.** Confirm the platform decision from Step 1 still holds after Step 2. Common mistakes: - Switching to `MSTest.Sdk` without `UseVSTest=true` silently flips a VSTest project to MTP. Add `<UseVSTest>true</UseVSTest>` to the project (the SDK pulls in `Microsoft.NET.Test.Sdk` automatically -- no manual `PackageReference` needed). - `<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>` only affects the `dotnet run` entry point and is **not** a runner switch in Test Explorer or `dotnet test`. Do not infer the platform from this property in either direction -- defer to the `platform-detection` skill (see Step 1). 2. Delete `xunit.runner.json` and port any settings you need (parallelization, `[CollectionBehavior]`, `appDomain`) per Step 11's "xunit.runner.json -> MSTest" sub-table. The settings have no direct MSBuild-property mapping. 3. Remove `using Xunit;` and `using Xunit.Abstractions;` from C# files. For **Option A** (`MSTest` metapackage), add `using Microsoft.VisualStudio.TestTools.UnitTesting;` per file (Step 4 covers this alongside the other rewrites). For **Option B** (`MSTest.Sdk`), skip the per-file using -- the SDK provides it as an implicit global using. ### Step 4: Convert test classes and methods Apply these rewrites to every C# test file. Class-level first, then method-level. **Class:** - Add `[TestClass]` to every class that contained xUnit `[Fact]`/`[Theory]` methods (xUnit had no class-level requirement). - **Preserve the original class hierarchy.** xUnit projects often use base/derived test classes (shared setup, helper assertions, generic base fixtures); marking classes `sealed` would break that pattern. Sealing is an optional follow-up handled by `writing-mstest-tests`, not part of the mechanical migration. - Replace `using Xunit;` / `using Xunit.Abstractions;` with `using Microsoft.VisualStudio.TestTools.UnitTesting;`. **On Option B (`MSTest.Sdk`), skip adding the MSTest using** -- the SDK provides it as an implicit global using, so just remove the `using Xunit;` / `using Xunit.Abstractions;` lines (Step 2 and Step 3 cover this). **Methods:** > **`[Ignore]` and `[Timeout]` are modifiers, not discovery attributes.** Always emit `[TestMethod]` *alongside* them -- a method with `[Ignore]` but no `[TestMethod]` is silently skipped by the test runner (no error, no skip count). Same for `[Timeout]`. | xUnit | MSTest | |---|---| | `[Fact]` | `[TestMethod]` | | `[Theory]` | `[TestMethod]` (parameterized; MSTest 3+ no longer needs `[DataTestMethod]`) | | `[Fact(DisplayName = "x")]` | `[TestMethod("x")]` (v3 of MSTest) or `[TestMethod(DisplayName = "x")]` (v4) | | `[Fact(Skip = "reason")]` | `[TestMethod]` + `[Ignore("reason")]` (both attributes required) | | `[Fact(Timeout = 5000)]` | `[TestMethod]` + `[Timeout(5000)]` (both attributes required) | | `[Trait("Category", "Unit")]` | `[TestCategory("Unit")]` |
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen