Skip to content

Add migrate-xunit-to-mstest skill from dotnet/skills#54727

Merged
Evangelink merged 3 commits into
dotnet:mainfrom
Evangelink:evangelink/add-migrate-xunit-to-mstest-skill
Jun 12, 2026
Merged

Add migrate-xunit-to-mstest skill from dotnet/skills#54727
Evangelink merged 3 commits into
dotnet:mainfrom
Evangelink:evangelink/add-migrate-xunit-to-mstest-skill

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Imports the migrate-xunit-to-mstest skill from dotnet/skills into this repo so the xUnit → MSTest migration currently in progress (see #54722, #54723, #54724, #54725, #54726) has a documented, repeatable procedure that contributors and agents can follow consistently.

What's added

  • .github/skills/migrate-xunit-to-mstest/SKILL.md — verbatim from upstream
  • .github/skills/migrate-xunit-to-mstest/references/mapping-cheatsheet.md — verbatim from upstream

Why land this first

These small migration PRs all follow the same mechanical mapping table ([Fact][TestMethod], Assert.EqualAssert.AreEqual, the Assert.Contains arg-reversal, StringValues ambiguity, Assert.Collection expansion, etc.). Centralizing the procedure as a discoverable skill means reviewers and future contributors don't need to rediscover the gotchas from each PR's description.

Validator note

.github/skills/ValidateSkill.cs flags SKILL.md as 546 lines (advisory limit is 500). The validator is not CI-enforced (no workflow consumes it; it's mentioned only as a checklist item in .github/skills/AGENTS.md). I'm intentionally mirroring upstream verbatim so future re-syncs stay trivial. If the team prefers a trimmed copy, happy to fold the longer sections (steps 8, 11, 12) into references/.

Source

Imports the migrate-xunit-to-mstest skill from dotnet/skills@main
(plugins/dotnet-test/skills/migrate-xunit-to-mstest) into this repo so
the xUnit -> MSTest migration that's currently in progress (dotnet#54722,
dotnet#54723, dotnet#54724, dotnet#54725, dotnet#54726) has a documented, repeatable procedure
that contributors and agents can follow consistently.

Files added (verbatim from upstream):
- .github/skills/migrate-xunit-to-mstest/SKILL.md
- .github/skills/migrate-xunit-to-mstest/references/mapping-cheatsheet.md

Note: SKILL.md is 546 lines, slightly above the 500-line "Progressive
Disclosure" guideline checked by .github/skills/ValidateSkill.cs. The
validator is advisory (not CI-enforced) and we are intentionally
mirroring the upstream content verbatim to keep future syncs trivial.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 11, 2026 19:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new agent skill to document and standardize the ongoing xUnit → MSTest migration workflow in this repo, imported from dotnet/skills, along with a mapping cheatsheet reference.

Changes:

  • Add .github/skills/migrate-xunit-to-mstest/SKILL.md describing an end-to-end migration workflow and decision points.
  • Add .github/skills/migrate-xunit-to-mstest/references/mapping-cheatsheet.md with detailed attribute/assert/fixture/lifecycle mapping tables.
Show a summary per file
File Description
.github/skills/migrate-xunit-to-mstest/SKILL.md Adds the primary migration skill workflow and guidance.
.github/skills/migrate-xunit-to-mstest/references/mapping-cheatsheet.md Adds the detailed xUnit→MSTest mapping reference used by the skill.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 4

Comment thread .github/skills/migrate-xunit-to-mstest/SKILL.md
Comment thread .github/skills/migrate-xunit-to-mstest/SKILL.md
Comment thread .github/skills/migrate-xunit-to-mstest/SKILL.md
Comment thread .github/skills/migrate-xunit-to-mstest/SKILL.md
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
- Bumps MSTest.Sdk to the latest internal preview to pick up the newest
  4.3 assertion APIs (Assert.ContainsSingle, Assert.Contains for strings
  with the more natural (needle, haystack) signature, etc.).
- Applies the assertion mapping flagged by the migrate-xunit-to-mstest
  skill in this repo (.github/skills/migrate-xunit-to-mstest, PR dotnet#54727):
    Assert.HasCount(1, x) -> Assert.ContainsSingle(x)
  (one occurrence in AspireResourceLauncherCliTests.cs)

Verified: 58/58 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…le(x)

Applies the assertion mapping flagged by the migrate-xunit-to-mstest skill
(.github/skills/migrate-xunit-to-mstest, PR dotnet#54727). Two occurrences in
ParserTests.cs converted from `Assert.HasCount(1, parseResult.Errors)` to
the more idiomatic `Assert.ContainsSingle(parseResult.Errors)`, which
matches the xUnit original (`Assert.Single`) more directly.

Verified: 9/9 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…le(x)

Applies the assertion mapping flagged by the migrate-xunit-to-mstest skill
(.github/skills/migrate-xunit-to-mstest, PR dotnet#54727). Two occurrences in
ParserTests.cs converted from `Assert.HasCount(1, parseResult.Errors)` to
the more idiomatic `Assert.ContainsSingle(parseResult.Errors)`, which
matches the xUnit original (`Assert.Single`) more directly.

Verified: 9/9 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…tain for strings

The migrate-xunit-to-mstest skill (.github/skills/migrate-xunit-to-mstest,
PR dotnet#54727) notes that MSTest 3.8+ added Assert.Contains(needle, str) /
Assert.DoesNotContain(needle, str) overloads for strings -- with the
natural (needle, haystack) argument order matching xUnit's
Assert.Contains. With the 4.3.0-preview MSTest.Sdk bump in dotnet#54722, these
are now available throughout the migration.

Replaced:
- StringAssert.Contains(haystack, needle)  -> Assert.Contains(needle, haystack)
- Assert.IsFalse(haystack.Contains(needle)) -> Assert.DoesNotContain(needle, haystack)

Touched files:
- BrowserRefreshMiddlewareTest.cs
- BrowserScriptMiddlewareTest.cs
- ResponseStreamWrapperCompressionTest.cs

Note: Assert.IsFalse(dict.ContainsKey(key)) call sites are unchanged
(no Assert.DoesNotContainKey API exists).

Verified: 110/110 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…le(x)

Applies the assertion mapping flagged by the migrate-xunit-to-mstest skill
(.github/skills/migrate-xunit-to-mstest, PR dotnet#54727). Two occurrences in
ParserTests.cs converted from `Assert.HasCount(1, parseResult.Errors)` to
the more idiomatic `Assert.ContainsSingle(parseResult.Errors)`, which
matches the xUnit original (`Assert.Single`) more directly.

Verified: 9/9 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…tain for strings

The migrate-xunit-to-mstest skill (.github/skills/migrate-xunit-to-mstest,
PR dotnet#54727) notes that MSTest 3.8+ added Assert.Contains(needle, str) /
Assert.DoesNotContain(needle, str) overloads for strings -- with the
natural (needle, haystack) argument order matching xUnit's
Assert.Contains. With the 4.3.0-preview MSTest.Sdk bump in dotnet#54722, these
are now available throughout the migration.

Replaced:
- StringAssert.Contains(haystack, needle)  -> Assert.Contains(needle, haystack)
- Assert.IsFalse(haystack.Contains(needle)) -> Assert.DoesNotContain(needle, haystack)

Touched files:
- BrowserRefreshMiddlewareTest.cs
- BrowserScriptMiddlewareTest.cs
- ResponseStreamWrapperCompressionTest.cs

Note: Assert.IsFalse(dict.ContainsKey(key)) call sites are unchanged
(no Assert.DoesNotContainKey API exists).

Verified: 110/110 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Folds learnings discovered while migrating dotnet/sdk to MSTest.Sdk:

- MSTest.Sdk implicit global using note
- Assert.IsExactInstanceOfType<T> (MSTest 4.1+) for xUnit Assert.IsType<T>
- Assert.AreSequenceEqual (MSTest 4.3+) over CollectionAssert.AreEqual
- AwesomeAssertions namespace clarification

Keeps the sdk-local copy in sync with the upstream PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
Member Author

Pushed an update folding learnings from the actual migration (PRs #54722-#54726) into the skill, mirroring dotnet/skills#756:

  • MSTest.Sdk provides Microsoft.VisualStudio.TestTools.UnitTesting as an implicit global using -- don't add <Using> items or per-file usings.
  • Assert.IsExactInstanceOfType<T> (MSTest 4.1+) is the proper xUnit Assert.IsType<T> equivalent.
  • Assert.AreSequenceEqual (MSTest 4.3+) over CollectionAssert.AreEqual for element-wise compare.
  • AwesomeAssertions ships in the FluentAssertions namespace (no source changes needed).

Once dotnet/skills#756 merges, this branch will be in sync with upstream.

… equivalence

Addresses review comment from copilot-pull-request-reviewer: the Step 6 summary table conflated xUnit's exact-type Assert.IsType<T> and assignable Assert.IsAssignableFrom<T> into a single row, which implies both map interchangeably even though their semantics differ.

Split into three rows -- IsType<T> -> IsExactInstanceOfType<T>, IsNotType<T> -> IsNotExactInstanceOfType<T>, IsAssignableFrom<T> -> IsInstanceOfType<T> -- and explicitly call out the silent-weakening trap.

The cheatsheet already mapped these correctly; this aligns the SKILL.md summary so the at-a-glance table doesn't suggest a wrong mapping. Mirrored in dotnet/skills#756.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/skills that referenced this pull request Jun 11, 2026
… equivalence

Address dotnet/sdk#54727 review comment: the Step 6 summary table conflated xUnit's exact-type Assert.IsType<T> and assignable Assert.IsAssignableFrom<T> into a single row, which implies both map interchangeably even though their semantics differ. Split into three rows -- IsType<T> -> IsExactInstanceOfType<T>, IsNotType<T> -> IsNotExactInstanceOfType<T>, IsAssignableFrom<T> -> IsInstanceOfType<T> -- and explicitly call out the silent-weakening trap.

The cheatsheet already maps these correctly; this aligns the SKILL.md summary with the cheatsheet so the at-a-glance table doesn't suggest a wrong mapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…le(x)

Applies the assertion mapping flagged by the migrate-xunit-to-mstest skill
(.github/skills/migrate-xunit-to-mstest, PR dotnet#54727). Two occurrences in
ParserTests.cs converted from `Assert.HasCount(1, parseResult.Errors)` to
the more idiomatic `Assert.ContainsSingle(parseResult.Errors)`, which
matches the xUnit original (`Assert.Single`) more directly.

Verified: 9/9 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to Evangelink/sdk that referenced this pull request Jun 11, 2026
…tain for strings

The migrate-xunit-to-mstest skill (.github/skills/migrate-xunit-to-mstest,
PR dotnet#54727) notes that MSTest 3.8+ added Assert.Contains(needle, str) /
Assert.DoesNotContain(needle, str) overloads for strings -- with the
natural (needle, haystack) argument order matching xUnit's
Assert.Contains. With the 4.3.0-preview MSTest.Sdk bump in dotnet#54722, these
are now available throughout the migration.

Replaced:
- StringAssert.Contains(haystack, needle)  -> Assert.Contains(needle, haystack)
- Assert.IsFalse(haystack.Contains(needle)) -> Assert.DoesNotContain(needle, haystack)

Touched files:
- BrowserRefreshMiddlewareTest.cs
- BrowserScriptMiddlewareTest.cs
- ResponseStreamWrapperCompressionTest.cs

Note: Assert.IsFalse(dict.ContainsKey(key)) call sites are unchanged
(no Assert.DoesNotContainKey API exists).

Verified: 110/110 tests still pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink added a commit to dotnet/skills that referenced this pull request Jun 12, 2026
* migrate-xunit-to-mstest: add learnings from dotnet/sdk migration

Refines the skill based on a real migration of 5 test projects in dotnet/sdk:

- MSTest.Sdk implicit global using: do not add `<Using Include='Microsoft.VisualStudio.TestTools.UnitTesting' />` or per-file `using` in MSTest.Sdk projects (it's already in scope). Note Option A (`MSTest` metapackage) still needs the per-file using.
- `Assert.IsExactInstanceOfType<T>` (MSTest 4.1+) is the proper single-call equivalent of xUnit's exact-type `Assert.IsType<T>`; previous guidance silently degraded it to assignable semantics (closes #755).
- `Assert.AreSequenceEqual` (MSTest 4.3+) is the modern element-wise equivalent of xUnit's `Assert.Equal` on `IEnumerable<T>`, avoiding the `CollectionAssert` + `.ToList()` dance and the MSTEST0065 trap on plain `AreEqual`.
- Clarify `AwesomeAssertions` ships in the `FluentAssertions` namespace, so it's a no-source-change swap.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* SKILL.md: split IsType/IsAssignableFrom row to avoid implying drop-in equivalence

Address dotnet/sdk#54727 review comment: the Step 6 summary table conflated xUnit's exact-type Assert.IsType<T> and assignable Assert.IsAssignableFrom<T> into a single row, which implies both map interchangeably even though their semantics differ. Split into three rows -- IsType<T> -> IsExactInstanceOfType<T>, IsNotType<T> -> IsNotExactInstanceOfType<T>, IsAssignableFrom<T> -> IsInstanceOfType<T> -- and explicitly call out the silent-weakening trap.

The cheatsheet already maps these correctly; this aligns the SKILL.md summary with the cheatsheet so the at-a-glance table doesn't suggest a wrong mapping.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* SKILL.md: resolve Step 3/Step 4 conflict on the MSTest using for Option B

Address review feedback on #756: Step 3 said to skip the
per-file 'using Microsoft.VisualStudio.TestTools.UnitTesting;' on
Option B (MSTest.Sdk provides it as an implicit global using), but
Step 4 unconditionally instructed replacing xUnit usings with that
MSTest using -- so an agent following Step 4 literally would re-add
the redundant using on Option B projects.

Also drop the dangling 'Step 4 rewriter' phrasing in Step 3 (there is
no rewriter in Step 4, just a list of mechanical rewrites the agent
applies).

Step 4 now explicitly says to skip the MSTest using on Option B and
only remove the 'using Xunit;' lines, matching Step 2's note and
Step 3's branch instructions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Evangelink <amaury@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink Evangelink merged commit bf132dc into dotnet:main Jun 12, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants