Skip to content

Add DependencyModules.xUnit4 for xunit.v3 4.x - #118

Merged
ipjohnson merged 2 commits into
mainfrom
feat/xunit4-package
Sep 25, 2026
Merged

ipjohnson merged 2 commits into
mainfrom
feat/xunit4-package

Conversation

@ipjohnson

@ipjohnson ipjohnson commented Sep 25, 2026 •

Copy link
Copy Markdown
Owner

This branch also carries #119, which was merged into it: ICurrentTestProvider, CurrentTest, and TestOutputLoggerProvider in DependencyModules.Testing. #119 describes that part.

What

  • DependencyModules.xUnit4, a new package that compiles the DependencyModules.xUnit sources against xunit.v3 [4.0.0,5.0.0), with its own assembly name. DependencyModules.xUnit stays on [3.2.2,4.0.0).
  • The code that differs between the majors is in Impl/ModuleTestCase.XunitMajor.cs: Run for each major, and CreateTest. On 4.x, CreateTest gives a row's Label and DisableParallelization to XunitTest, as [Theory] does. The discoverer needs no #if, because the same label: null call binds to the overload of each major.
  • XunitMajor in Directory.Build.props selects the xunit package, the runner, and the DependencyModules project for the three xUnit test projects. scripts/test-xunit.sh 4 runs them on 4.x, in build-package and in release.
  • xunit-prerelease.yaml runs them each week on the newest xunit.v3 on nuget.org, prereleases included.
  • verify-packages.sh packs the new package. It now fails if a test package's framework range does not stop below the next major.
  • Docs: the xUnit page gives the package for each major and a note about dotnet test on 4.x. The README, the testing overview, and CONTRIBUTING are updated.

Why

The plan in #117 was a run-time lookup for GetTestCaseDetails and a wider range. Built against 3.2.2 with that lookup, every [ModuleTest] on 4.0.1 then failed at discovery:

System.TypeLoadException: Method 'Run' in type 'DependencyModules.xUnit.Impl.ModuleTestCase' from assembly 'DependencyModules.xUnit, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.

4.0 added ParallelMode, ExecutionScheduler, and FixtureMappingManager parameters to ISelfExecutingXunitTestCase.Run. The six-parameter XunitRunnerHelper.RunXunitTestCase that Run calls is [Obsolete(error: true)] in 4.x and throws NotSupportedException. A 3.2.2 build cannot declare the new Run, and a run-time lookup cannot supply an interface method. A Mono.Cecil pass over every xUnit reference in the 3.2.2 build found these two, the known GetTestCaseDetails overload, and one constructor that 4.x only marks obsolete.

One design keeps a single package: stop being self-executing and dispose in PostInvoke. All suites passed on both majors that way. But PostInvoke is synchronous, so it has to block on DisposeAsync. A probe service that disposes through the context it was created on hung under the Aggressive algorithm, on both majors. The awaited Run disposes the same service in 5 ms. So, as #117 says for this case, 4.x gets its own line.

Notes

  • The 4.x test leg uses xunit.v3.mtp-off. The Microsoft.Testing.Platform v2 that xunit.v3 4.x brings refuses the VSTest dotnet test on the .NET 10 SDK, and xUnit ships mtp-off for that case.
  • The prereleases carried each 4.0 break. The GetTestCaseDetails overload went in 4.0.0-pre.81 (2026-04-12), the old RunXunitTestCase started to throw in pre.108 (2026-05-02), and Run changed in pre.154 (2026-07-17). The weekly workflow would have failed on each.
  • Hardened.Shared.Testing.xUnit 0.39 has a 4.x break of its own: its XunitCurrentTestProvider reads IXunitTestClass.Class, which 4.0 moved to ICoreTestClass. Add CurrentTest and TestOutputLoggerProvider to DependencyModules.Testing #119, now merged into this branch, gives Hardened a framework-neutral seam, so it can drop [HardenedTest].
  • No CHANGELOG entry. The cut commit carries it.

Verified

  • scripts/coverage.sh 85: 1069, 161, 39, and 2 tests pass on net8.0 and net10.0, at 92.3% line coverage.
  • scripts/test-xunit.sh 4: 1070, 161, and 2 tests pass on both TFMs, on xunit.v3.mtp-off 4.0.1 with runner 4.0.0.
  • scripts/verify-packages.sh, dotnet csharpier check ., and npm run build pass.
  • The API snapshot of the 4.x build differs from the 3.x one in two lines: ICoreTestCase in the interfaces of ModuleTestCase, and the three new Run parameters.
  • With Add CurrentTest and TestOutputLoggerProvider to DependencyModules.Testing #119 merged in, scripts/coverage.sh 85 passes 1081, 171, 46, and 2 tests at 92.3% line coverage, and scripts/test-xunit.sh 4 passes 1082, 171, and 2.

Closes #117.

🤖 Generated with Claude Code

xunit.v3 4.0 changed ISelfExecutingXunitTestCase.Run and made the old
RunXunitTestCase throw. A ModuleTestCase built against 3.x therefore
does not load on 4.x, and a run-time lookup cannot supply a missing
interface method.

DependencyModules.xUnit stays on [3.2.2,4.0.0). DependencyModules.xUnit4
compiles the same sources against [4.0.0,5.0.0), and
Impl/ModuleTestCase.XunitMajor.cs holds the code that differs.

The xUnit test projects run on both majors through XunitMajor. The 4.x
leg uses xunit.v3.mtp-off, because Microsoft.Testing.Platform v2 refuses
VSTest dotnet test on the .NET 10 SDK. A weekly workflow runs the tests
on the newest xunit.v3, prereleases included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ting (#119)

Each runner package installs an ICurrentTestProvider over its test
framework's own context: TestContext.Current for xUnit, and
TestExecutionContext.CurrentContext for NUnit. A library can then read
the test that runs, and write to its output, with no reference to a test
framework. Hardened can drop [HardenedTest] and its two runner packages
for [ModuleTest].

xUnit's output helper throws when a background task writes after its
test has finished. The xUnit provider drops that line, so a logger does
not throw into the application under test.

Co-authored-by: Ian Johnson <ianjohnson@mac.mynetworksettings.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ipjohnson
ipjohnson merged commit 828bdbe into main Sep 25, 2026
2 checks passed
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.

Let DependencyModules.xUnit run on xunit.v3 3.x and 4.x

1 participant