Skip to content

Migrate from xUnit to TUnit and adopt Microsoft.Testing.Platform v2#48

Merged
BenjaminMichaelis merged 3 commits into
mainfrom
benjaminmichaelis/tunit-migration-plan
May 17, 2026
Merged

Migrate from xUnit to TUnit and adopt Microsoft.Testing.Platform v2#48
BenjaminMichaelis merged 3 commits into
mainfrom
benjaminmichaelis/tunit-migration-plan

Conversation

@BenjaminMichaelis
Copy link
Copy Markdown
Owner

Why

TUnit is the modern test framework for .NET with source generation, AOT compatibility, and native support for Microsoft.Testing.Platform v2 (MTP v2). This migration:

  • Standardizes on MTP v2 for the .NET 10 SDK (native dotnet test support, no VSTest bridge required)
  • Removes legacy dependencies (xUnit, coverlet) in favor of TUnit's built-in MTP coverage support
  • Follows the official TUnit xUnit migration guide for maximum compatibility and maintainability

Approach

Framework Conversion (using TUnit fixer):

  • Installed TUnit alongside xUnit temporarily, disabled implicit usings, ran dotnet format analyzers --severity info --diagnostics TUXU0001 to auto-convert attributes
  • [Fact][Test], [Theory][Test], [InlineData][Arguments], [MemberData][MethodDataSource]

Custom xUnit Infrastructure Replacement:

  • Deleted ConditionalFactAttribute.cs and ConditionalFactDiscoverer.cs (xUnit SDK dependent, not portable)
  • Created OperatingSystemSkipAttributes.cs with WindowsOnlyAttribute and NonWindowsOnlyAttribute using TUnit's native SkipAttribute pattern
  • Updated 4 platform-specific tests to use new skip attributes

Data Source Fix:

  • Fixed TrxParserRegressionTests.cs data source: converted TheoryData<> initialization to tuple-based IEnumerable<(string, string[])> (valid syntax for TUnit method data sources)

Project Runtime Changes:

  • Added <OutputType>Exe</OutputType> (required for MTP v2 executables)
  • Removed xunit, xunit.runner.visualstudio, Microsoft.NET.Test.Sdk, coverlet.collector
  • Updated global.json with MTP v2 runner opt-in for .NET 10
  • Updated CI test command to use MTP-native coverage flags (--coverage --coverage-output-format cobertura)

Result

  • All 63 tests passing; 2 platform-specific tests correctly skipped on Windows
  • MTP coverage command produces Cobertura XML output as expected
  • No breaking changes to test behavior, only framework/runner swap

Testing

  • Baseline: 61 xUnit tests passing
  • Post-migration: 63 TUnit tests passing (added 2 by fixing method data source return type), 2 platform-conditional skips working as intended
  • Coverage flags validated and producing output

- Convert all test attributes: [Fact] -> [Test], [Theory] -> [Test] with [Arguments], [InlineData] -> [Arguments], [MemberData] -> [MethodDataSource]
- Replace xUnit custom conditional attribute infrastructure with TUnit-native skip logic:
  - Delete ConditionalFactAttribute.cs and ConditionalFactDiscoverer.cs (xUnit SDK dependent)
  - Add OperatingSystemSkipAttributes.cs with WindowsOnlyAttribute and NonWindowsOnlyAttribute
- Switch project to run as executable for MTP v2:
  - Add <OutputType>Exe</OutputType> to TrxLib.Tests.csproj
  - Remove xunit, xunit.runner.visualstudio, coverlet.collector, Microsoft.NET.Test.Sdk packages
  - Keep TUnit for test framework
- Enable MTP v2 runner in global.json for .NET 10 SDK native support
- Update CI test command to use MTP-native coverage flags (--coverage --coverage-output-format cobertura)
- Fix data source conversion in TrxParserRegressionTests: convert TheoryData<> to tuple-based IEnumerable

All 63 tests passing; 2 platform-specific tests conditionally skipped on Windows as intended.
Copilot AI review requested due to automatic review settings May 17, 2026 14:18
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Migrates the test project from xUnit/VSTest to TUnit on Microsoft.Testing.Platform v2 (MTP v2), updating test attributes/data sources and aligning CI to run tests with MTP-native coverage flags.

Changes:

  • Convert xUnit [Fact]/[Theory]/[InlineData]/[MemberData] usage to TUnit [Test]/[Arguments]/[MethodDataSource].
  • Replace custom xUnit ConditionalFact infrastructure with TUnit-native OS skip attributes (WindowsOnly / NonWindowsOnly).
  • Update test project/CI to use MTP v2 (test project OutputType=Exe, global.json runner opt-in, CI coverage flags) and remove legacy test/coverage packages.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
TrxLib.Tests/TrxParserTests.cs Converts tests to TUnit attributes and swaps platform-conditional tests to WindowsOnly/NonWindowsOnly.
TrxLib.Tests/TrxParserRegressionTests.cs Updates theory/data-source pattern to TUnit arguments + method data source friendly shape.
TrxLib.Tests/TestResultTests.cs Converts parameterized and non-parameterized tests to TUnit attributes.
TrxLib.Tests/OperatingSystemSkipAttributes.cs Adds TUnit SkipAttribute-based OS gating attributes to replace xUnit ConditionalFact.
TrxLib.Tests/TrxLib.Tests.csproj Switches package references from xUnit/VSTest/coverlet to TUnit and sets OutputType=Exe for MTP v2.
TrxLib.Tests/ConditionalFactDiscoverer.cs Removes xUnit conditional discovery infrastructure.
TrxLib.Tests/ConditionalFactAttribute.cs Removes xUnit ConditionalFact attribute.
global.json Opts into Microsoft.Testing.Platform runner selection for dotnet test.
Directory.Packages.props Drops xUnit/VSTest/coverlet versions and adds TUnit version.
.github/workflows/build-and-test.yml Updates CI dotnet test invocation to include MTP-native coverage flags.
Comments suppressed due to low confidence (1)

TrxLib.Tests/TrxParserTests.cs:142

  • IsWindows() / NotWindows() helpers (and the using System.Runtime.InteropServices; at the top of the file) are now unused after switching to [WindowsOnly] / [NonWindowsOnly]. Please remove the dead methods/usings to keep the test file tidy and avoid unused-member warnings from analyzers.
    [Test, WindowsOnly]
    public void Parse_Example1WindowsTrx_ParsesTestProjectDirectoryCorrectly()
    {
        var results = TrxParser.Parse(new FileInfo(GetSampleFilePath(Path.Combine("example1_Windows.trx"))));
        var test = results.Single(r => r.FullyQualifiedTestName == "Microsoft.DotNet.Cli.Sln.Internal.Tests.GivenAnSlnFile.WhenGivenAValidPathItReadsModifiesThenWritesAnSln");

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…1OSXTrx_ParsesTestProjectDirectoryCorrectly

The DirectoryInfo.FullName property doesn't include a trailing slash on any platform, so the test assertion should not expect one. The previous test had a trailing slash which was inconsistent with the Windows test assertion and doesn't match the actual behavior of DirectoryInfo.FullName.

This fix aligns the test with the actual code behavior that uses FindProjectDirectory, which returns a DirectoryInfo object without a trailing path separator.
Prevents the workflow from cancelling other matrix jobs (like windows-latest) when one job (like ubuntu-latest) fails. This allows all matrix jobs to complete even if some fail, providing more comprehensive CI results.
@BenjaminMichaelis BenjaminMichaelis merged commit 0af743d into main May 17, 2026
6 checks passed
@BenjaminMichaelis BenjaminMichaelis deleted the benjaminmichaelis/tunit-migration-plan branch May 17, 2026 15:54
github-actions Bot pushed a commit to BenjaminMichaelis/VS.TestPlaylistTools that referenced this pull request May 19, 2026
[//]: # (dependabot-start)
⚠️  **Dependabot is rebasing this PR** ⚠️ 

Rebasing might not happen immediately, so don't worry if this takes some
time.

Note: if you make any changes to this PR yourself, they will take
precedence over the rebase.

---

[//]: # (dependabot-end)

Updated [TrxLib](https://github.com/BenjaminMichaelis/TrxLib) from 0.0.3
to 1.0.0.

<details>
<summary>Release notes</summary>

_Sourced from [TrxLib's
releases](https://github.com/BenjaminMichaelis/TrxLib/releases)._

## 1.0.0

## Features
- Cleaned up some TRX file bugs
- Now AOT compliant!
- Cleaned up a lot of misc tech debt

## What's Changed
* Bump actions/checkout from 5 to 6 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#20
* Bump actions/upload-artifact from 5 to 6 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#22
* Bump Microsoft.SourceLink.GitHub from 8.0.0 to 10.0.102 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#23
* Bump coverlet.collector from 6.0.4 to 8.0.0 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#24
* Bump Microsoft.SourceLink.GitHub from 10.0.102 to 10.0.103 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#25
* Bump AwesomeAssertions from 9.3.0 to 9.4.0 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#26
* Bump actions/download-artifact from 6 to 8 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#28
* Bump IntelliTect.Multitool from 1.5.3 to 2.0.0 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#29
* Bump Microsoft.NET.Test.Sdk from 18.0.1 to 18.3.0 by @​dependabot[bot]
in BenjaminMichaelis/TrxLib#30
* Bump Microsoft.SourceLink.GitHub from 10.0.103 to 10.0.201 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#31
* Bump coverlet.collector from 8.0.0 to 8.0.1 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#32
* Bump fastify/github-action-merge-dependabot from 3.11.2 to 3.12.0 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#33
* Bump Microsoft.NET.Test.Sdk from 18.3.0 to 18.4.0 by @​dependabot[bot]
in BenjaminMichaelis/TrxLib#34
* Bump Microsoft.SourceLink.GitHub from 10.0.201 to 10.0.202 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#36
* Bump coverlet.collector from 8.0.1 to 10.0.0 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#35
* Bump Microsoft.SourceLink.GitHub from 10.0.202 to 10.0.203 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#37
* Bump Microsoft.NET.Test.Sdk from 18.4.0 to 18.5.1 by @​dependabot[bot]
in BenjaminMichaelis/TrxLib#38
* feat: Migrate NuGet publish to trusted publishing (OIDC) by
@​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#39
* Migrate to slnx solution file format by @​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#40
* fix: TRX parser data-loss bugs and FQTN derivation spec compliance by
@​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#42
* fix: Refactor TestOutcome enum with updated summaries by
@​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#43
* fix: add missing vstest outcomes and fix directory heuristic for RID
paths by @​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#46
* fix: missing vstest outcomes, TestProjectDirectory heuristic, xmlns
fallback by @​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#47
* Migrate from xUnit to TUnit and adopt Microsoft.Testing.Platform v2 by
@​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#48
* Remove AwesomeAssertions, use TUnit built-in assertions by
@​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#49
* Bump IntelliTect.Multitool from 2.0.0 to 2.1.0 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#50
* Bump TUnit from 1.44.39 to 1.45.0 by @​dependabot[bot] in
BenjaminMichaelis/TrxLib#52
* Bump Microsoft.SourceLink.GitHub from 10.0.203 to 10.0.300 by
@​dependabot[bot] in BenjaminMichaelis/TrxLib#51
* chore: align project configuration with NuGet library template best
practices by @​BenjaminMichaelis in
BenjaminMichaelis/TrxLib#53


**Full Changelog**:
BenjaminMichaelis/TrxLib@v0.0.3...v1.0.0

Commits viewable in [compare
view](BenjaminMichaelis/TrxLib@v0.0.3...v1.0.0).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=TrxLib&package-manager=nuget&previous-version=0.0.3&new-version=1.0.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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.

2 participants