Skip to content

Add CurrentTest and TestOutputLoggerProvider to DependencyModules.Testing - #119

Merged
ipjohnson merged 1 commit into
feat/xunit4-packagefrom
feat/current-test
Sep 25, 2026
Merged

ipjohnson merged 1 commit into
feat/xunit4-packagefrom
feat/current-test

Conversation

@ipjohnson

Copy link
Copy Markdown
Owner

Stacked on #118. Merge that first. I will rebase this branch onto main when it merges.

What

  • ICurrentTestProvider in DependencyModules.Testing.Attributes.Interfaces: Key, DisplayName, Assembly, and TryWriteLine for the test that runs.
  • CurrentTest in DependencyModules.Testing.Impl: a static accessor that reads through CurrentTest.Provider.
  • TestOutputLoggerProvider in DependencyModules.Testing.Impl: an ILoggerProvider that writes each entry to the output of the test that runs, in the single-line format of the console logger (info: Category[0] message).
  • Each runner installs its provider from static constructors, before its first test runs. XunitCurrentTestProvider reads TestContext.Current in both xUnit builds. NUnitCurrentTestProvider reads TestExecutionContext.CurrentContext.
  • DependencyModules.Testing takes Microsoft.Extensions.Logging.Abstractions, framework-matched at 8.0.0 and 10.0.0.
  • A guide page, "CurrentTest and test output", and entries in the testing overview, the xUnit and NUnit pages, the API reference, and the README.

Why

Hardened.Shared.Testing.xUnit and Hardened.Shared.Testing.NUnit exist to supply three things: [HardenedTest], a provider over the framework's context, and a logger for the test output. With the provider and the logger here, Hardened.Shared.Testing reads CurrentTest, and its users write [ModuleTest] from the runner package they pick. [HardenedTest] adds nothing to [ModuleTest] except the provider install, which the runners now do. Hardened then needs no release for a new xUnit major.

Notes

  • Key is the framework's own object for the test, as Hardened uses today: TestContext.Current.Test on xUnit and the NUnit Test on NUnit. The framework holds it while the test runs, so a weak table can key per-test state on it.
  • On xUnit, no test runs while a case builds its containers. In the hooks that build them, Key and DisplayName are null and TryWriteLine returns false. On NUnit they have values there. Tests pin both, and the docs say so.
  • NUnit makes no object for one iteration, so the iterations of a [Repeat] or [Retry] share one Key. Each iteration still gets its own container.
  • xUnit's output helper stays on a background flow after its test ends, and WriteLine then throws InvalidOperationException: There is no currently active test. The xUnit provider catches it. With the catch removed, a pair of tests fails with that exception. Hardened's current XUnitLogger has no such catch.
  • The providers are internal. The xUnit, xUnit4, and NUnit API snapshots do not change. The Testing snapshot adds the three types.

Verified, together with #118

  • scripts/coverage.sh 85: 1081, 171, 46, and 2 tests pass on net8.0 and net10.0, at 92.3% line coverage.
  • scripts/test-xunit.sh 4: 1082, 171, and 2 tests pass on both TFMs.
  • scripts/verify-packages.sh, dotnet csharpier check ., and npm run build pass.

After the release

Hardened replaces its own CurrentTest with this one, deletes its two runner packages, and changes [HardenedTest] to [ModuleTest] in its tests, the hardened-web template, and its docs (ipjohnson/Hardened.Framework#400).

🤖 Generated with Claude Code

…ting

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: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ipjohnson
ipjohnson merged commit da52b83 into feat/xunit4-package Sep 25, 2026
ipjohnson added a commit that referenced this pull request Sep 25, 2026
* Add DependencyModules.xUnit4 for xunit.v3 4.x

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>

* Add CurrentTest and TestOutputLoggerProvider to DependencyModules.Testing (#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>

---------

Co-authored-by: Ian Johnson <ianjohnson@mac.mynetworksettings.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.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.

1 participant