Skip to content

Migrate Microsoft.AspNetCore.Watch.BrowserRefresh.Tests to MSTest.Sdk on MTP - #54725

Merged
Evangelink merged 32 commits into
dotnet:mainfrom
Evangelink:evangelink/mstest-mtp-browserrefresh-tests
Jun 22, 2026
Merged

Migrate Microsoft.AspNetCore.Watch.BrowserRefresh.Tests to MSTest.Sdk on MTP#54725
Evangelink merged 32 commits into
dotnet:mainfrom
Evangelink:evangelink/mstest-mtp-browserrefresh-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Same pattern as #54723 / #54724. None of the 6 test files use 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

  • csproj: Sdk="MSTest.Sdk", UseMSTestSdk=true, drop TestFramework ref + OutputType=Exe, add Microsoft.VisualStudio.TestTools.UnitTesting 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/NotEqualIsTrue/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.CancellationTokenCancellationToken.None (MSTest's TestContext.Current is experimental; these unit tests do not actually require test-cancellation propagation).

Verification

110/110 tests pass on MTP (~480 ms).

Merge ordering

Stacks on #54724 which stacks on #54723 which stacks on #54722.

Relates to #52914.

Copilot AI review requested due to automatic review settings June 11, 2026 19:35
@Evangelink
Evangelink requested review from a team and tmat as code owners June 11, 2026 19:35

@tmat tmat left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why are we migrating away from XUnit?

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

Migrates additional SDK test projects from xUnit to MSTest.Sdk (on Microsoft.Testing.Platform), dropping the xUnit-coupled Microsoft.NET.TestFramework where it isn’t needed and updating assertions/attributes accordingly.

Changes:

  • Add MSTest.Sdk to global.json and gate the repo’s xUnit-specific test defaults behind UseMSTestSdk.
  • Convert several unit test projects (notably Microsoft.AspNetCore.Watch.BrowserRefresh.Tests) to MSTest attributes/asserts and remove the TestFramework ProjectReference.
  • Update dotnet-watch.Tests to support Aspire integration tests (new helper + project reference + IVT).
Show a summary per file
File Description
global.json Adds MSTest.Sdk msbuild SDK version pin.
test/Directory.Build.targets Gates xUnit defaults behind UseMSTestSdk opt-out.
test/containerize.UnitTests/containerize.UnitTests.csproj Switches project to MSTest.Sdk, removes TestFramework reference.
test/containerize.UnitTests/ParserTests.cs Converts xUnit tests to MSTest attributes/asserts.
test/Microsoft.DotNet.ApiCompat.Tests/Microsoft.DotNet.ApiCompat.Tests.csproj Switches project to MSTest.Sdk, removes TestFramework reference, adds MSTest global using.
test/Microsoft.DotNet.ApiCompat.Tests/RegexStringTransformerTests.cs Converts xUnit tests to MSTest attributes/asserts.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/Microsoft.DotNet.HotReload.Watch.Aspire.Tests.csproj Switches project to MSTest.Sdk, removes xUnit-coupled test utilities reference.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AssertEx.cs Adds a small local assertion helper to avoid TestFramework dependency.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireServerLauncherCliTests.cs Converts xUnit tests to MSTest attributes/asserts.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireResourceLauncherCliTests.cs Converts xUnit tests to MSTest attributes/asserts (incl. DataRow conversions).
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireHostLauncherTests.cs Converts xUnit tests to MSTest attributes/asserts.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireHostLauncherCliTests.cs Converts xUnit tests to MSTest attributes/asserts.
src/Dotnet.Watch/Watch.Aspire/Properties/AssemblyInfo.cs Adds InternalsVisibleTo for dotnet-watch.Tests.
test/dotnet-watch.Tests/dotnet-watch.Tests.csproj Adds Watch.Aspire project reference needed by Aspire integration tests.
test/dotnet-watch.Tests/Aspire/AspireLauncherIntegrationTests.cs Renames integration test class for clarity.
test/dotnet-watch.Tests/Aspire/PipeUtilities.cs Adds new helper for reading Aspire status events via named pipes.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests.csproj Switches BrowserRefresh test project to MSTest.Sdk, removes TestFramework reference.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/ScriptInjectingStreamTests.cs Converts xUnit tests to MSTest attributes/asserts and adjusts cancellation handling.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/ResponseStreamWrapperCompressionTest.cs Converts xUnit tests to MSTest attributes/asserts and string/sequence asserts.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/HostingStartupTest.cs Converts xUnit tests to MSTest attributes/asserts and resolves StringValues comparisons.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/BrowserScriptMiddlewareTest.cs Converts xUnit tests to MSTest attributes/asserts and expands Assert.Collection.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/BrowserRefreshMiddlewareTest.cs Converts xUnit tests to MSTest attributes/asserts and updates string containment checks.
test/Microsoft.AspNetCore.Watch.BrowserRefresh.Tests/BlazorWasmHotReloadMiddlewareTest.cs Converts xUnit tests to MSTest attributes/asserts and sequence comparisons.

Copilot's findings

  • Files reviewed: 22/23 changed files
  • Comments generated: 1

Comment thread test/containerize.UnitTests/containerize.UnitTests.csproj Outdated
Comment thread test/containerize.UnitTests/containerize.UnitTests.csproj Outdated
@Evangelink
Evangelink marked this pull request as draft June 11, 2026 19:52
@Evangelink
Evangelink force-pushed the evangelink/mstest-mtp-browserrefresh-tests branch from 3f1079f to 77b601f Compare June 11, 2026 20:18
@Evangelink

Copy link
Copy Markdown
Member Author

Re-validated this PR against the new migrate-xunit-to-mstest skill landed in #54727 and bumped MSTest.Sdk to 4.3.0-preview.26311.10 (latest internal preview) via the pathfinder.

Changes applied in the latest push on this branch:
StringAssert.Contains(haystack, needle) -> Assert.Contains(needle, haystack) -- uses MSTest 3.8+/4.3 overload with the natural xUnit-style (needle, haystack) order; Assert.IsFalse(str.Contains(x)) -> Assert.DoesNotContain(x, str). 12+ string assertion sites cleaned up.

Other gotchas reviewed (no changes needed here):

  • Assert.AreEqual argument order — verified (expected, actual) throughout, matching the xUnit Assert.Equal(expected, actual) original.
  • CollectionAssert argument order — verified.
  • Assert.ThrowsExactly<T> vs Assert.Throws<T> semantics — verified (xUnit Throws<T> is exact; MSTest's same-named API is inclusive, so the migration uses ThrowsExactly<T>).
  • IsInstanceOfType<T> vs IsExactInstanceOfType<T> — see dotnet/skills#755 for the cheatsheet update we filed upstream.

Tests still pass locally on the bumped MSTest.Sdk.

@Evangelink
Evangelink force-pushed the evangelink/mstest-mtp-browserrefresh-tests branch 2 times, most recently from c1a2f7f to 6835289 Compare June 11, 2026 21:04
Evangelink added a commit that referenced this pull request Jun 12, 2026
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>
@Evangelink

Copy link
Copy Markdown
Member Author

Replaced the manual <UseMSTestSdk>true</UseMSTestSdk> opt-in with the property MSTest.Sdk already sets for us: UsingMSTestSdk. Since MSTest.Sdk's Sdk.props runs before Microsoft.NET.Sdk.props and test/Directory.Build.targets is imported at the bottom of evaluation, the property is in scope wherever we need it — no per-project knob required.

Also refreshed the related comments in XUnitPublish.targets / XUnitRunner.targets and renamed the intermediate _UseMSTestSdk MSBuild property to _UsingMSTestSdk for clarity. (Thanks @Evangelink for the catch.)

@Evangelink

Copy link
Copy Markdown
Member Author

Made --report-trx conditional on Microsoft.Testing.Extensions.TrxReport actually being loaded. MSTest.Sdk enables that extension by default via EnableMicrosoftTestingExtensionsTrxReport=true for the Default / AllMicrosoft profiles, but other MTP hosts (xUnit v3 MTP, MSTest.Sdk with TestingExtensionsProfile=None, etc.) do not — passing --report-trx there fails with 'unknown argument'.

The new GetTrxReportEnabled target queries that property; XUnitRunner.targets propagates it as EnableTrxReport metadata, and SDKCustomCreateXUnitWorkItemsWithTestExclusion only emits the flag when it's true. No behavioural change for the current PRs since all migrated projects are MSTest.Sdk-based, but the dispatcher is now forward-safe.

(Applied to all 9 stacked PRs since they share these plumbing files.)

@Evangelink

Copy link
Copy Markdown
Member Author

Replaced the custom <UseMSTestSdk> opt-in property with the built-in UsingMSTestSdk property that MSTest.Sdk already sets in its Sdk.props before Directory.Build.props is evaluated. This drops a redundant declaration in every migrated csproj and just reads what the SDK already publishes.

@Evangelink

Copy link
Copy Markdown
Member Author

Also removed <IsPackable>false</IsPackable> from the migrated csproj — MSTest.Sdk's Runner/Common.targets already sets it, so the per-project declaration is redundant.

@Evangelink

Copy link
Copy Markdown
Member Author

Switched migration-introduced CancellationToken.None back to a cancellable token by adding the standard MSTest public TestContext TestContext { get; set; } property on the test class and using TestContext.CancellationToken — this preserves the original xUnit v3 behavior (TestContext.Current.CancellationToken) and keeps tests cancellable. The TestContext.Current static would also work but is currently marked experimental (MSTESTEXP).

@Evangelink

Copy link
Copy Markdown
Member Author

Round 3: moved FluentAssertions Using and AwesomeAssertions PackageReference to test/Directory.Build.targets.

The Using is now global for any test project ('$(IsTestProject)' == 'true' OR '$(UsingMSTestSdk)' == 'true'). The PackageReference is added for MSTest.Sdk projects only — xUnit projects already get it transitively via the Microsoft.NET.TestFramework project reference.

Deviation from your literal suggestion: I left the Microsoft.NET.TestFramework.* Usings and Xunit Using gated on UsingMSTestSdk != true. Moving them unconditional would also require a global <ProjectReference Include="Microsoft.NET.TestFramework" /> (otherwise MSTest projects would fail with CS0246), which would drag xunit.v3.assert and other xUnit pieces into the MSTest projects. Happy to apply the broader change (with a global ProjectRef) if you'd prefer.

Evangelink and others added 2 commits June 12, 2026 18:03
…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>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
Member Author

Done — updated to assert on actualUpdate.Deltas.Length.

…EST0049)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink and others added 2 commits June 17, 2026 09:10
…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)
…fresh PR

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink
Evangelink enabled auto-merge (squash) June 17, 2026 11:13
@Evangelink

Copy link
Copy Markdown
Member Author

/ba-g #54813 #54818

The CI failures on this PR are not related to its changes. This PR migrates Microsoft.AspNetCore.Watch.BrowserRefresh.Tests to MSTest.Sdk/MTP; that project passed on every leg. All 3 failed Helix work items are in unrelated xUnit projects, which do not consume MSTest.Sdk and so cannot be affected by this PR's global.json MSTest.Sdk bump or the migrated test helpers:

@Evangelink

Copy link
Copy Markdown
Member Author

/azp run dotnet-sdk-public-ci

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

The four 2-arg WriteAsync calls originally flowed TestContext.Current.CancellationToken
in the xUnit source; restore the test cancellation token via the MSTest TestContext
property rather than weakening it to CancellationToken.None. The two 3-arg calls already
used CancellationToken.None on main and are left unchanged.

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

Copy link
Copy Markdown
Member Author

/ba-g #54826

I have closed previous tickets as they were wrongly/partially filled

@Evangelink

Copy link
Copy Markdown
Member Author

@tmat would you reviewing/approving PR?

@Evangelink

Copy link
Copy Markdown
Member Author

/azp run dotnet-sdk-public-ci

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@Evangelink

Copy link
Copy Markdown
Member Author

/ba-g #54819

@Evangelink
Evangelink merged commit 6dc369e into dotnet:main Jun 22, 2026
24 of 26 checks passed
@Evangelink
Evangelink deleted the evangelink/mstest-mtp-browserrefresh-tests branch June 22, 2026 16:41
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone Jun 24, 2026
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.

4 participants