Migrate Microsoft.DotNet.ApiCompat.Tests to MSTest.Sdk on MTP - #54724
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates several test projects from xUnit-based defaults to MSTest.Sdk (running on Microsoft.Testing.Platform) by introducing a per-project opt-out switch (UseMSTestSdk=true) and mechanically converting affected tests/asserters. It also adjusts dotnet-watch.Tests to host Aspire-related integration tests that still depend on Microsoft.NET.TestFramework.
Changes:
- Add
MSTest.Sdktoglobal.jsonand gate the test-folder xUnit defaults intest/Directory.Build.targetsbehindUseMSTestSdk. - Convert multiple unit test projects to
Sdk="MSTest.Sdk"and update test attributes/assertions accordingly (including a localAssertExhelper where needed). - Move/enable Aspire integration-test support in
dotnet-watch.Testsvia additional references andInternalsVisibleTo.
Show a summary per file
| File | Description |
|---|---|
| global.json | Pins MSTest.Sdk MSBuild SDK version so Sdk="MSTest.Sdk" projects restore/build. |
| test/Directory.Build.targets | Gates xUnit-specific defaults/usings behind UseMSTestSdk to allow per-project MSTest adoption. |
| test/Microsoft.DotNet.ApiCompat.Tests/Microsoft.DotNet.ApiCompat.Tests.csproj | Switches ApiCompat test project to MSTest.Sdk and adds MSTest global using. |
| test/Microsoft.DotNet.ApiCompat.Tests/RegexStringTransformerTests.cs | Converts xUnit [Fact]/Assert.Equal to MSTest [TestMethod]/Assert.AreEqual. |
| test/containerize.UnitTests/containerize.UnitTests.csproj | Switches to MSTest.Sdk, opts out of xUnit defaults, removes Microsoft.NET.TestFramework reference. |
| test/containerize.UnitTests/ParserTests.cs | Converts xUnit tests to MSTest equivalents ([DataRow], Assert.AreEqual, etc.). |
| test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/Microsoft.DotNet.HotReload.Watch.Aspire.Tests.csproj | Switches the Aspire unit test project to MSTest.Sdk, drops xUnit-coupled test utilities, adds assertion packages/usings. |
| test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AssertEx.cs | Adds a small local assertion helper to avoid depending on xUnit-coupled shared test utilities. |
| test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireServerLauncherCliTests.cs | Converts xUnit tests to MSTest ([TestClass], [TestMethod], MSTest asserts). |
| test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireResourceLauncherCliTests.cs | Converts xUnit tests to MSTest, including theory/data conversion to [DataRow]. |
| test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireHostLauncherTests.cs | Converts xUnit tests to MSTest and updates asserts accordingly. |
| test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireHostLauncherCliTests.cs | Converts xUnit tests to MSTest and updates asserts accordingly. |
| src/Dotnet.Watch/Watch.Aspire/Properties/AssemblyInfo.cs | Grants InternalsVisibleTo for dotnet-watch.Tests to support moved integration tests. |
| test/dotnet-watch.Tests/dotnet-watch.Tests.csproj | Adds a reference to Watch.Aspire for Aspire integration tests that execute the launcher. |
| test/dotnet-watch.Tests/Aspire/PipeUtilities.cs | Adds helper for reading status events from a named pipe in integration tests. |
| test/dotnet-watch.Tests/Aspire/AspireLauncherIntegrationTests.cs | Renames the Aspire launcher test class to reflect its integration-test nature. |
Copilot's findings
- Files reviewed: 15/16 changed files
- Comments generated: 1
71c989f to
c6d27b7
Compare
… on MTP
Same pattern as previous migrations. None of the 6 test files used any
Microsoft.NET.TestFramework types - only xUnit primitives - so the
TestFramework ProjectReference can be dropped and the project switched to
MSTest.Sdk in one self-contained change.
Changes:
* test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/csproj:
- Use Sdk="MSTest.Sdk", set UseMSTestSdk=true.
- Drop ProjectReference to Microsoft.NET.TestFramework (unused).
- Drop OutputType=Exe (MSTest.Sdk handles it).
- Add Microsoft.VisualStudio.TestTools.UnitTesting global using.
* 6 test files: xUnit -> MSTest mechanical conversion plus targeted fixes:
- [Fact]/[Theory] -> [TestMethod], [InlineData] -> [DataRow], add [TestClass].
- Assert.True/False/Null/NotNull/Empty/NotEmpty/Equal/NotEqual ->
IsTrue/IsFalse/IsNull/IsNotNull/IsEmpty/IsNotEmpty/AreEqual/AreNotEqual.
- Assert.Single(x) -> Assert.HasCount(1, x).
- String Assert.Contains(needle, haystack) -> StringAssert.Contains(haystack, needle)
(xUnit and MSTest argument orders are reversed).
- One Assert.DoesNotContain(needle, str) -> Assert.IsFalse(str.Contains(needle)).
- Assert.Collection in BrowserScriptMiddlewareTest expanded to inline
Assert.HasCount + indexed AreEqual checks.
- Assert.ThrowsAsync<T> -> Assert.ThrowsExactlyAsync<T>.
- Assert.AreEqual on byte[]/int[] -> Assert.AreSequenceEqual (MSTEST0065:
MSTest does not do element-wise equality on collections; the analyzer
catches a real semantic difference vs xUnit).
- StringValues (Microsoft.Extensions.Primitives) ambiguity with
Assert.AreEqual<T> resolved by calling .ToString() at 5 call sites.
- xUnit v3 TestContext.Current.CancellationToken -> CancellationToken.None
(MSTest's TestContext.Current is experimental; tests do not actually
require test-cancellation propagation).
Verification: 110/110 tests pass on MTP in ~480 ms.
Stacks on dotnet#54724 which stacks on dotnet#54723 which stacks on dotnet#54722.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Re-validated this PR against the new Changes applied in the latest push on this branch: Other gotchas reviewed (no changes needed here):
Tests still pass locally on the bumped MSTest.Sdk. |
c6d27b7 to
d5fdfd0
Compare
… on MTP
Same pattern as previous migrations. None of the 6 test files used any
Microsoft.NET.TestFramework types - only xUnit primitives - so the
TestFramework ProjectReference can be dropped and the project switched to
MSTest.Sdk in one self-contained change.
Changes:
* test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/csproj:
- Use Sdk="MSTest.Sdk", set UseMSTestSdk=true.
- Drop ProjectReference to Microsoft.NET.TestFramework (unused).
- Drop OutputType=Exe (MSTest.Sdk handles it).
- Add Microsoft.VisualStudio.TestTools.UnitTesting global using.
* 6 test files: xUnit -> MSTest mechanical conversion plus targeted fixes:
- [Fact]/[Theory] -> [TestMethod], [InlineData] -> [DataRow], add [TestClass].
- Assert.True/False/Null/NotNull/Empty/NotEmpty/Equal/NotEqual ->
IsTrue/IsFalse/IsNull/IsNotNull/IsEmpty/IsNotEmpty/AreEqual/AreNotEqual.
- Assert.Single(x) -> Assert.HasCount(1, x).
- String Assert.Contains(needle, haystack) -> StringAssert.Contains(haystack, needle)
(xUnit and MSTest argument orders are reversed).
- One Assert.DoesNotContain(needle, str) -> Assert.IsFalse(str.Contains(needle)).
- Assert.Collection in BrowserScriptMiddlewareTest expanded to inline
Assert.HasCount + indexed AreEqual checks.
- Assert.ThrowsAsync<T> -> Assert.ThrowsExactlyAsync<T>.
- Assert.AreEqual on byte[]/int[] -> Assert.AreSequenceEqual (MSTEST0065:
MSTest does not do element-wise equality on collections; the analyzer
catches a real semantic difference vs xUnit).
- StringValues (Microsoft.Extensions.Primitives) ambiguity with
Assert.AreEqual<T> resolved by calling .ToString() at 5 call sites.
- xUnit v3 TestContext.Current.CancellationToken -> CancellationToken.None
(MSTest's TestContext.Current is experimental; tests do not actually
require test-cancellation propagation).
Verification: 110/110 tests pass on MTP in ~480 ms.
Stacks on dotnet#54724 which stacks on dotnet#54723 which stacks on dotnet#54722.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
d5fdfd0 to
4cf42fa
Compare
… on MTP
Same pattern as previous migrations. None of the 6 test files used any
Microsoft.NET.TestFramework types - only xUnit primitives - so the
TestFramework ProjectReference can be dropped and the project switched to
MSTest.Sdk in one self-contained change.
Changes:
* test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/csproj:
- Use Sdk="MSTest.Sdk", set UseMSTestSdk=true.
- Drop ProjectReference to Microsoft.NET.TestFramework (unused).
- Drop OutputType=Exe (MSTest.Sdk handles it).
- Add Microsoft.VisualStudio.TestTools.UnitTesting global using.
* 6 test files: xUnit -> MSTest mechanical conversion plus targeted fixes:
- [Fact]/[Theory] -> [TestMethod], [InlineData] -> [DataRow], add [TestClass].
- Assert.True/False/Null/NotNull/Empty/NotEmpty/Equal/NotEqual ->
IsTrue/IsFalse/IsNull/IsNotNull/IsEmpty/IsNotEmpty/AreEqual/AreNotEqual.
- Assert.Single(x) -> Assert.HasCount(1, x).
- String Assert.Contains(needle, haystack) -> StringAssert.Contains(haystack, needle)
(xUnit and MSTest argument orders are reversed).
- One Assert.DoesNotContain(needle, str) -> Assert.IsFalse(str.Contains(needle)).
- Assert.Collection in BrowserScriptMiddlewareTest expanded to inline
Assert.HasCount + indexed AreEqual checks.
- Assert.ThrowsAsync<T> -> Assert.ThrowsExactlyAsync<T>.
- Assert.AreEqual on byte[]/int[] -> Assert.AreSequenceEqual (MSTEST0065:
MSTest does not do element-wise equality on collections; the analyzer
catches a real semantic difference vs xUnit).
- StringValues (Microsoft.Extensions.Primitives) ambiguity with
Assert.AreEqual<T> resolved by calling .ToString() at 5 call sites.
- xUnit v3 TestContext.Current.CancellationToken -> CancellationToken.None
(MSTest's TestContext.Current is experimental; tests do not actually
require test-cancellation propagation).
Verification: 110/110 tests pass on MTP in ~480 ms.
Stacks on dotnet#54724 which stacks on dotnet#54723 which stacks on dotnet#54722.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 (#54722, #54723, #54724, #54725, #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>
|
Replaced the manual Also refreshed the related comments in |
|
Made The new (Applied to all 9 stacked PRs since they share these plumbing files.) |
|
Replaced the custom |
|
Round 3: moved The Using is now global for any test project ( Deviation from your literal suggestion: I left the |
…n MTP
This is a pathfinder PR for migrating the test suite to MSTest on
Microsoft.Testing.Platform (MTP). Microsoft.DotNet.HotReload.Watch.Aspire.Tests
was chosen because it has no dependency on the shared Microsoft.NET.TestFramework
(which is xUnit-coupled and referenced by ~57 of 78 test projects), so it can
migrate in isolation without unblocking dependents first.
Changes:
* global.json: add MSTest.Sdk 4.3.0-preview.26307.5 to msbuild-sdks.
* test/Directory.Build.targets: gate the xUnit defaults
(TestRunnerName=XUnitV3, Using Include=Xunit, etc.) behind
$(UseMSTestSdk) != true, so MSTest.Sdk projects opt out cleanly.
* test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests:
- csproj now uses Sdk="MSTest.Sdk", sets UseMSTestSdk=true, references
AwesomeAssertions and only the Watch.Aspire project. MTP is on by default
via MSTest.Sdk (EnableMSTestRunner + TestingPlatformDotnetTestSupport).
- All 4 unit-test files converted from xUnit to MSTest attributes/asserts
([Fact]/[Theory] -> [TestMethod]/[DataRow], Assert.* equivalents,
Assert.IsInstanceOfType<T>, Assert.HasCount, Assert.IsEmpty).
- Local AssertEx.SequenceEqual<T> helper replaces the xUnit-coupled one
from HotReload.Test.Utilities.
* Move the 2 integration tests (AspireLauncherTests + PipeUtilities) to
test/dotnet-watch.Tests/Aspire/ so the Aspire.Tests project stays a pure
MSTest unit-test project. They keep xUnit because they depend on
WatchSdkTest, WatchableApp, [PlatformSpecificFact], ITestOutputHelper and
TestAssets from Microsoft.NET.TestFramework. AspireLauncherTests was
renamed to AspireLauncherIntegrationTests to reflect its new role.
* src/Dotnet.Watch/Watch.Aspire/Properties/AssemblyInfo.cs: grant
InternalsVisibleTo to dotnet-watch.Tests (needed by PipeUtilities, which
uses internal WatchStatusEvent).
* test/dotnet-watch.Tests/dotnet-watch.Tests.csproj: add ProjectReference to
Watch.Aspire (ExcludeAssets=Runtime) so the moved integration tests
compile.
Verification:
* Microsoft.DotNet.HotReload.Watch.Aspire.Tests builds with MSTest.Sdk and
all 58 unit tests pass under MTP (705 ms).
* test/dotnet-watch.Tests builds successfully with the moved files.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- 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>
…parity Per reviewer feedback: MSTest 4.1.0+ exposes Assert.IsExactInstanceOfType<T>(value) which returns T and enforces exact-type semantics -- the proper equivalent of xUnit's Assert.IsType<T>(x). Assert.IsInstanceOfType<T> is the equivalent of xUnit's Assert.IsAssignableFrom<T> (assignable, not exact), which would be a silent semantic regression for the IsType<T> originals. All 39 occurrences across AspireHostLauncherCliTests.cs, AspireResourceLauncherCliTests.cs, AspireServerLauncherCliTests.cs, and AspireLauncherIntegrationTests.cs were originally Assert.IsType<T> in xUnit (verified against main), so all 39 are flipped to Assert.IsExactInstanceOfType<T>. Note: the migrate-xunit-to-mstest skill cheatsheet at .github/skills/migrate-xunit-to-mstest/references/mapping-cheatsheet.md recommends `Assert.IsInstanceOfType<T>` plus an extra typeof-check for exact-type semantics; that guidance predates IsExactInstanceOfType being available. Follow-up upstream (dotnet/skills) suggested. Verified: 58/58 Aspire tests still pass; dotnet-watch.Tests builds clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
AspireLauncherIntegrationTests exec the Aspire launcher (Microsoft.DotNet.HotReload.Watch.Aspire) as a child process. ExcludeAssets=Runtime omitted the launcher's transitive runtime dependencies (Microsoft.CodeAnalysis*) from the test output, so the launched process crashed at startup with FileNotFoundException and the tests failed with Assert.NotNull. Removing ExcludeAssets=Runtime deploys the full closure next to the launcher. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run dotnet-sdk-public-ci |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…tionTests) PR dotnet#54766 enabled the Recommended MSTest analyzers as errors; dotnet#54758 merged shortly after with violations that are now build errors, leaving main red and blocking all open migration PRs. Fix the two affected files: - CommandResultAssertions.MSTest.cs: MSTEST0037 (IsTrue(a==b)->AreEqual, IsFalse(a==b) ->AreNotEqual) and MSTEST0023 (IsTrue(!x)->IsFalse(x)). - LocalizeTemplateTests.cs: MSTEST0037 (AreEqual(n, x.Length)->HasCount(n, x)). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 6ac4ef0)
Imports the already-verified MSTEST0037 HasCount fixes for the stale lower-stack copies (containerize ParserTests, Aspire.Tests) so this stacked PR builds clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Same pattern as #54723. This project's single test file (
RegexStringTransformerTests.cs) only uses xUnitFact/Assert.Equal— noMicrosoft.NET.TestFrameworktypes — so the TestFrameworkProjectReferencecan be dropped and the project switched toMSTest.Sdkin one self-contained change.Changes
test/Microsoft.DotNet.ApiCompat.Tests/Microsoft.DotNet.ApiCompat.Tests.csprojSdk="MSTest.Sdk", setUseMSTestSdk=true.ProjectReferencetoMicrosoft.NET.TestFramework(unused).OutputType=Exe(MSTest.Sdk handles it).Microsoft.VisualStudio.TestTools.UnitTestingglobal using.test/Microsoft.DotNet.ApiCompat.Tests/RegexStringTransformerTests.cs:[Fact]→[TestMethod], add[TestClass],Assert.Equal→Assert.AreEqual.Verification
✅ 5/5 tests pass on MTP (~250 ms).
Merge ordering
Stacks on top of #54723 which stacks on #54722. Once both merge, this PR will rebase cleanly onto
main.Relates to #52914.