Skip to content

Migrate Microsoft.Extensions.DotNetDeltaApplier.Tests to MSTest.Sdk#54730

Open
Evangelink wants to merge 13 commits into
dotnet:mainfrom
Evangelink:evangelink/mstest-mtp-deltaapplier-tests
Open

Migrate Microsoft.Extensions.DotNetDeltaApplier.Tests to MSTest.Sdk#54730
Evangelink wants to merge 13 commits into
dotnet:mainfrom
Evangelink:evangelink/mstest-mtp-deltaapplier-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Summary

  • Builds on Migrate Microsoft.DotNet.HotReload.Watch.Aspire.Tests to MSTest.Sdk on MTP (pathfinder) #54722 to migrate test/Microsoft.Extensions.DotNetDeltaApplier.Tests from xUnit to MSTest.Sdk on MTP.
  • Sets UseMSTestSdk=true, replaces xUnit attributes/assertions with MSTest equivalents, and removes the Microsoft.NET.TestFramework project reference.
  • Replaces Xunit.Combinatorial with Combinatorial.MSTest 2.0.0 and adds the Dependabot package version.
  • Replaces ITestOutputHelper usage with MSTest TestContext through a project-local TestLogger.

Validation

  • Q:\src\sdk\.dotnet\dotnet.exe build test\Microsoft.Extensions.DotNetDeltaApplier.Tests\Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj (0 warnings, 0 errors)
  • Q:\src\sdk\.dotnet\dotnet.exe exec artifacts\bin\Microsoft.Extensions.DotNetDeltaApplier.Tests\Debug\net11.0\Microsoft.Extensions.DotNetDeltaApplier.Tests.dll (121 passed)

TestFX issues

  • None filed.

@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

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.

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 Microsoft.Extensions.DotNetDeltaApplier.Tests (and updates related watch/Aspire tests) from xUnit to MSTest.Sdk on Microsoft.Testing.Platform, and updates Helix dispatching to correctly execute MTP-based test assemblies.

Changes:

  • Convert multiple test projects/files from xUnit attributes/asserts to MSTest equivalents and introduce an MSTest TestContext-based TestLogger.
  • Update Helix/xunit-runner infrastructure to detect MTP + TRX capability and switch execution from dotnet test to dotnet exec when needed.
  • Add/adjust dependencies and build configuration for MSTest.Sdk + combinatorial data and update internals visibility for moved integration tests.
Show a summary per file
File Description
test/xunit-runner/XUnitRunner.targets Captures per-project metadata to detect MTP and TRX capability for Helix work item generation.
test/xunit-runner/XUnitPublish.targets Adds MSBuild targets exposing MTP/TRX detection values to the xunit-runner pipeline.
test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs Switches Helix command-line to dotnet exec for MTP projects and conditionally adds --report-trx.
test/Directory.Build.targets Gates xUnit-only global usings and adds AwesomeAssertions for MSTest.Sdk projects.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Microsoft.Extensions.DotNetDeltaApplier.Tests.csproj Migrates project to MSTest.Sdk, updates references, and adds combinatorial MSTest dependency.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/Mocks/TestLogger.cs Adds MSTest-friendly logger that writes via TestContext.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/StreamExtensionsTests.cs Converts combinatorial tests and assertions from xUnit to MSTest.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/StaticAssetUpdateRequestTests.cs Converts test attributes/assertions from xUnit to MSTest.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/ManagedCodeUpdateRequestTests.cs Converts test attributes/assertions from xUnit to MSTest, including sequence asserts.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/HotReloadClientTests.cs Replaces ITestOutputHelper with TestContext and updates assertions.
test/Microsoft.Extensions.DotNetDeltaApplier.Tests/HotReloadAgentTest.cs Converts the suite to MSTest, including OS gating and updated assert APIs.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/Microsoft.DotNet.HotReload.Watch.Aspire.Tests.csproj Switches Aspire unit tests to MSTest.Sdk and simplifies references.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireServerLauncherCliTests.cs Converts xUnit tests/asserts to MSTest equivalents.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireResourceLauncherCliTests.cs Converts xUnit tests/asserts to MSTest equivalents and preserves FluentAssertions usage.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireHostLauncherTests.cs Converts xUnit tests/asserts to MSTest equivalents.
test/Microsoft.DotNet.HotReload.Watch.Aspire.Tests/AspireHostLauncherCliTests.cs Converts xUnit tests/asserts to MSTest equivalents.
test/dotnet-watch.Tests/dotnet-watch.Tests.csproj Adds reference needed for moved Aspire launcher integration tests.
test/dotnet-watch.Tests/Aspire/PipeUtilities.cs Introduces helper for reading Watch status events from a named pipe (integration tests support).
test/dotnet-watch.Tests/Aspire/AspireLauncherIntegrationTests.cs Renames/moves Aspire launcher integration test class (stays on xUnit).
src/Dotnet.Watch/Watch.Aspire/Properties/AssemblyInfo.cs Adds InternalsVisibleTo for dotnet-watch.Tests to support moved integration tests.
global.json Pins MSTest.Sdk MSBuild SDK version.
eng/dependabot/Packages.props Adds Dependabot-managed version pin for Combinatorial.MSTest.

Copilot's findings

  • Files reviewed: 21/22 changed files
  • Comments generated: 3

Comment on lines +48 to +52
public IDisposable? BeginScope<TState>(TState state)
where TState : notnull => throw new NotImplementedException();

public bool IsEnabled(LogLevel logLevel)
=> IsEnabledImpl(logLevel);
<Project Sdk="MSTest.Sdk">

<PropertyGroup>
<RestoreAdditionalProjectSources>$(RestoreAdditionalProjectSources);https://api.nuget.org/v3/index.json</RestoreAdditionalProjectSources>
Comment on lines +259 to +267
string envPrefix;
if (IsPosixShell)
{
envPrefix = $"HELIX_WORK_ITEM_TIMEOUT={timeout} ";
}
else
{
envPrefix = $"set HELIX_WORK_ITEM_TIMEOUT={timeout}&& ";
}
Evangelink and others added 7 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>
…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>
MSTest.Sdk already adds this as an implicit global using.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MSTest 4.3+ provides Assert.AreSequenceEqual for element-wise IEnumerable<T> compare with a nice diff message, so the project-local AssertEx helper is no longer needed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…est/Directory.Build.targets

Per @Evangelink: keep per-csproj boilerplate minimal. FluentAssertions is now a
global using for any test project (gated on IsTestProject OR UsingMSTestSdk),
and AwesomeAssertions is added as a PackageReference for MSTest.Sdk projects.
xUnit projects continue to pick it up transitively via Microsoft.NET.TestFramework.

The Microsoft.NET.TestFramework.* and Xunit usings remain gated on the xUnit
branch (UsingMSTestSdk != true) because MSTest projects in this repo do not
reference Microsoft.NET.TestFramework; making those usings global would fail
with CS0246.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The recent `Helix dispatcher: gate --report-trx on TrxReport extension
being loaded` commit accidentally removed the ProjectReference to
Microsoft.DotNet.HotReload.Watch.Aspire from dotnet-watch.Tests.csproj
(introduced in the `Migrate Microsoft.DotNet.HotReload.Watch.Aspire.Tests
to MSTest.Sdk on MTP` commit to allow the moved AspireLauncherIntegrationTests
and PipeUtilities to compile).

Without that reference, the build fails with:

    error CS0246: The type or namespace name 'WatchStatusEvent' could not be
    found (are you missing a using directive or an assembly reference?)
    [test/dotnet-watch.Tests/Aspire/PipeUtilities.cs]

Re-add the ProjectReference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink Evangelink force-pushed the evangelink/mstest-mtp-deltaapplier-tests branch from c394e6b to 63340ab Compare June 12, 2026 16:11
Evangelink and others added 6 commits June 12, 2026 18:29
Introduces an MSTest-flavored counterpart to the xUnit-based
Microsoft.DotNet.HotReload.Test.Utilities so that test helpers that
need MSTest's TestContext (e.g. TestLogger, TestLoggerFactory) can be
shared across MSTest.Sdk test projects instead of being copy-pasted
per project.

This commit also migrates the inline TestLogger from
Microsoft.DotNet.HotReload.Client.Tests to consume the shared project,
which serves as the first reference consumer. Subsequent migration PRs
(DeltaApplier.Tests, Containers.UnitTests) will adopt the same project
reference instead of adding their own TestLogger/TestLoggerFactory copies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Switch SDK to MSTest.Sdk; set UseMSTestSdk=true to opt out of test/Directory.Build.targets xUnit defaults.
- Replace [Fact]/[Theory] + [InlineData] with [TestMethod] + [DataRow].
- Replace Xunit.Combinatorial with Combinatorial.MSTest 2.0.0 (added to eng/dependabot/Packages.props).
- Replace ITestOutputHelper with TestContext via a project-local TestLogger.
- Replace xUnit assertions with MSTest assertions and remove the Microsoft.NET.TestFramework reference.

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

MSTest.Sdk already sets $(UsingMSTestSdk)=true in its Sdk.props before
Directory.Build.props is evaluated, so the custom <UseMSTestSdk>true</UseMSTestSdk>
opt-in property is redundant. This change:

- Removes <UseMSTestSdk>true</UseMSTestSdk> from MSTest.Sdk csproj(s).
- Renames $(UseMSTestSdk) -> $(UsingMSTestSdk) in test/Directory.Build.targets
  (the xUnit-defaults gating condition) and in xunit-runner/{XUnitPublish,XUnitRunner}.targets
  (Helix MTP dispatcher detection).
--report-trx is an MTP CLI argument provided only when the
Microsoft.Testing.Extensions.TrxReport extension is loaded on the test host.
MSTest.Sdk's Default/AllMicrosoft profiles enable it by default, but other MTP
runners (e.g. xUnit v3 MTP) do not bundle the extension, so passing --report-trx
to those hosts fails with 'unknown argument'.

XUnitPublish.targets now exposes the GetTrxReportEnabled target which returns
the value of EnableMicrosoftTestingExtensionsTrxReport. XUnitRunner.targets
calls that target and propagates the value as the EnableTrxReport metadata of
SDKCustomXUnitProject items. SDKCustomCreateXUnitWorkItemsWithTestExclusion
reads that metadata and only appends --report-trx to the MTP command line when
it is true.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The recent `Helix dispatcher: gate --report-trx on TrxReport extension
being loaded` commit accidentally removed the ProjectReference to
Microsoft.DotNet.HotReload.Watch.Aspire from dotnet-watch.Tests.csproj
(introduced in the `Migrate Microsoft.DotNet.HotReload.Watch.Aspire.Tests
to MSTest.Sdk on MTP` commit to allow the moved AspireLauncherIntegrationTests
and PipeUtilities to compile).

Without that reference, the build fails with:

    error CS0246: The type or namespace name 'WatchStatusEvent' could not be
    found (are you missing a using directive or an assembly reference?)
    [test/dotnet-watch.Tests/Aspire/PipeUtilities.cs]

Re-add the ProjectReference.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the local Mocks/TestLogger.cs copy with a project reference to
the shared MSTest utilities project introduced in the Aspire migration
PR (dotnet#54722), keeping a single source of truth for the TestLogger
implementation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink Evangelink force-pushed the evangelink/mstest-mtp-deltaapplier-tests branch from 63340ab to 2049acb Compare June 12, 2026 16:31
@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

/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

/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

/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

/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

/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

/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

/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

/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

/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

/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

/azp run dotnet-sdk-public-ci

@azure-pipelines

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants