chore(tests): upgrade NUnit 3 to 4.6.1 across all test projects - #239
Draft
ottobolyos wants to merge 4 commits into
Draft
chore(tests): upgrade NUnit 3 to 4.6.1 across all test projects#239ottobolyos wants to merge 4 commits into
ottobolyos wants to merge 4 commits into
Conversation
ottobolyos
force-pushed
the
chore/nunit-4-upgrade
branch
from
August 19, 2026 22:06
5e205e3 to
1623569
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 19, 2026
ottobolyos
force-pushed
the
chore/nunit-4-upgrade
branch
4 times, most recently
from
August 21, 2026 14:30
5d41ebf to
6d923ab
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
# Conflicts: # tests/MTConnect.NET-Common-Tests/V2_6_V2_7/V2_7DataItemTypeTests.cs # tests/MTConnect.NET-JSON-cppagent-Tests/Streams/JsonConditionsArrayShapeTests.cs
…ests Prior state fragmented four test csprojs at NUnit 3.13.3 and NUnit3TestAdapter 4.3.1/4.5.0 while the other five sat at NUnit 3.14.0 and NUnit3TestAdapter 4.6.0. Normalizing to the highest currently-in-use versions clears the drift without changing observable behavior and sets a clean baseline for the subsequent NUnit 3 -> 4 upgrade commit. Affected projects: * tests/Compliance/MTConnect-Compliance-Tests * tests/MTConnect.NET-AgentModule-MqttRelay-Tests * tests/MTConnect.NET-JSON-Tests * tests/MTConnect.NET-JSON-cppagent-Tests The remaining five test csprojs already sat at the target versions. Full-solution build (dotnet build -p:IntegrationCoverage=true): 0 warnings, 0 errors.
NUnit 4 is the current stable line; this commit lifts every test csproj onto
4.6.1 and applies the API-surface migrations the upgrade requires.
## Package bumps
All 9 test csprojs move NUnit 3.14.0 to 4.6.1:
* tests/Compliance/MTConnect-Compliance-Tests
* tests/MTConnect.NET-AgentModule-MqttRelay-Tests
* tests/MTConnect.NET-Common-Tests
* tests/MTConnect.NET-Docs-Tests
* tests/MTConnect.NET-HTTP-Tests
* tests/MTConnect.NET-JSON-Tests
* tests/MTConnect.NET-JSON-cppagent-Tests
* tests/MTConnect.NET-SHDR-Tests
* tests/MTConnect.NET-XML-Tests
NUnit3TestAdapter stays at 4.6.0 (adapter version tracks the adapter's own
release line, not the framework version).
## Compiler-driven source migrations
### CS0121 -- delegate-overload ambiguity (84 sites, 20 files)
NUnit 4 added Action / Func<T> overloads alongside the legacy TestDelegate /
ActualValueDelegate overloads for Assert.DoesNotThrow, Assert.Throws<T>,
Assert.ThrowsAsync<T>, Assert.Multiple, and Assert.That. Lambda arguments are
now ambiguous under overload resolution. Each affected call site casts the
lambda to (Action) so the compiler picks the new Action overload
unambiguously. Assert.That sites are cast only when the constraint is a
Throws.* form (void-lambda variant); the value-returning form (Is.*) is not
affected in this corpus.
Where the cast introduced a bare 'Action' reference in files that only
transitively imported the System namespace via other usings, 'using System;'
was added.
### CS9202 -- Assert.AreEqual extension-method dispatch (3 sites, 2 files)
NUnit 4 removed the classic Assert.AreEqual static method and replaced it
with an extension-method shim on Assert exposed by NUnit.Framework.Legacy.
The extension-method shim requires C# 14 (feature 'extensions'), which the
test projects do not target. The safe migration path is
ClassicAssert.AreEqual from NUnit.Framework.Legacy, which is a plain static
method and works under every C# language version. Two files switch the
callers accordingly and add 'using NUnit.Framework.Legacy;'.
Affected:
* tests/MTConnect.NET-Common-Tests/Agents/AgentUuidDeterministicDefaultTests.cs (1 site)
* tests/MTConnect.NET-XML-Tests/XmlStreamsResponseDocumentTests.cs (2 error sites; 3 total AreEqual calls in the same file swept together)
## Verification
Full-solution build (dotnet build -p:IntegrationCoverage=true):
0 Warning(s)
0 Error(s)
Ultrareview cycle 1 MEDIUM fix. Four independent agents (code-review F-CR-001, improvement F-IMP-002, simplification F-SIMP-001/F-SIMP-002) converged on the same recommendation: the three ClassicAssert.AreEqual sites left in the two files can move to the constraint-based Assert.That(actual, Is.EqualTo(expected)) idiom that the rest of the NUnit 4 upgrade already uses, which drops the only NUnit.Framework.Legacy imports the previous commit introduced and keeps a single assertion vocabulary across the test corpus. The prior commit chose ClassicAssert.AreEqual to sidestep NUnit 4s Assert.AreEqual extension-method shim (which requires C# 14). That sidestep is unnecessary: Assert.That has been NUnit s recommended constraint form since 3.x and has no C# 14 dependency, so it lands without any Legacy indirection. Behavior parity for value-type equality (string, int) is identical between ClassicAssert.AreEqual and Assert.That(x, Is.EqualTo(y)) at these sites; verified via focused test runs (both filters green). Sites changed: * tests/MTConnect.NET-Common-Tests/Agents/AgentUuidDeterministicDefaultTests.cs (1 site, drop using NUnit.Framework.Legacy) * tests/MTConnect.NET-XML-Tests/XmlStreamsResponseDocumentTests.cs (2 sites, drop using NUnit.Framework.Legacy) Full-solution build: 0 warnings, 0 errors.
ottobolyos
force-pushed
the
chore/nunit-4-upgrade
branch
from
August 21, 2026 16:06
6d923ab to
8646c4d
Compare
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…r-239 # Conflicts: # tests/MTConnect.NET-Common-Tests/V2_6_V2_7/V2_7DataItemTypeTests.cs # tests/MTConnect.NET-JSON-cppagent-Tests/Streams/JsonConditionsArrayShapeTests.cs
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…it 4 NUnit 4's Assert.DoesNotThrow / Assert.Throws<T> resolve delegate arguments against multiple overloads (Action, TestDelegate, AsyncTestDelegate). A bare () => lambda triggers CS0121 overload ambiguity once TrakHound#239 lands the NUnit 4 upgrade. The (Action) cast disambiguates unambiguously in both NUnit 3 and 4, so the wrap is safe to land on this branch before TrakHound#239 merges. Fixes cross-PR bug class discovered on integration/up-to-pr-249 build (158 errors, 112 CS0121 across 9 test files on 7 PRs). Per-PR fix — each affected PR wraps its own new test sites so the class stays clean across the train.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…it 4 NUnit 4's Assert.DoesNotThrow / Assert.Throws<T> resolve delegate arguments against multiple overloads (Action, TestDelegate, AsyncTestDelegate). A bare () => lambda triggers CS0121 overload ambiguity once TrakHound#239 lands the NUnit 4 upgrade. The (Action) cast disambiguates unambiguously in both NUnit 3 and 4, so the wrap is safe to land on this branch before TrakHound#239 merges. Fixes cross-PR bug class discovered on integration/up-to-pr-249 build (158 errors, 112 CS0121 across 9 test files on 7 PRs). Per-PR fix — each affected PR wraps its own new test sites so the class stays clean across the train.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…it 4 NUnit 4's Assert.DoesNotThrow / Assert.Throws<T> resolve delegate arguments against multiple overloads (Action, TestDelegate, AsyncTestDelegate). A bare () => lambda triggers CS0121 overload ambiguity once TrakHound#239 lands the NUnit 4 upgrade. The (Action) cast disambiguates unambiguously in both NUnit 3 and 4, so the wrap is safe to land on this branch before TrakHound#239 merges. Fixes cross-PR bug class discovered on integration/up-to-pr-249 build (158 errors, 112 CS0121 across 9 test files on 7 PRs). Per-PR fix — each affected PR wraps its own new test sites so the class stays clean across the train.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…it 4 NUnit 4's Assert.DoesNotThrow / Assert.Throws<T> resolve delegate arguments against multiple overloads (Action, TestDelegate, AsyncTestDelegate). A bare () => lambda triggers CS0121 overload ambiguity once TrakHound#239 lands the NUnit 4 upgrade. The (Action) cast disambiguates unambiguously in both NUnit 3 and 4, so the wrap is safe to land on this branch before TrakHound#239 merges. Fixes cross-PR bug class discovered on integration/up-to-pr-249 build (158 errors, 112 CS0121 across 9 test files on 7 PRs). Per-PR fix — each affected PR wraps its own new test sites so the class stays clean across the train.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…it 4 NUnit 4's Assert.DoesNotThrow / Assert.Throws<T> resolve delegate arguments against multiple overloads (Action, TestDelegate, AsyncTestDelegate). A bare () => lambda triggers CS0121 overload ambiguity once TrakHound#239 lands the NUnit 4 upgrade. The (Action) cast disambiguates unambiguously in both NUnit 3 and 4, so the wrap is safe to land on this branch before TrakHound#239 merges. Fixes cross-PR bug class discovered on integration/up-to-pr-249 build (158 errors, 112 CS0121 across 9 test files on 7 PRs). Per-PR fix — each affected PR wraps its own new test sites so the class stays clean across the train.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…it 4 NUnit 4's Assert.DoesNotThrow / Assert.Throws<T> resolve delegate arguments against multiple overloads (Action, TestDelegate, AsyncTestDelegate). A bare () => lambda triggers CS0121 overload ambiguity once TrakHound#239 lands the NUnit 4 upgrade. The (Action) cast disambiguates unambiguously in both NUnit 3 and 4, so the wrap is safe to land on this branch before TrakHound#239 merges. Fixes cross-PR bug class discovered on integration/up-to-pr-249 build (158 errors, 112 CS0121 across 9 test files on 7 PRs). Per-PR fix — each affected PR wraps its own new test sites so the class stays clean across the train.
NUnit 4 removed the AsyncTestDelegate overload for Assert.That in favour of Func<Task> — see 'Use Func<Task> instead of AsyncTestDelegate' obsoletion in nunit.framework.dll 4.6.1. CA2022ShortReadEdgeCaseTests.cs (arrived on master via merged TrakHound#219) has two Assert.That(async () => ..., Throws.*) sites that trigger CS0121 overload ambiguity once NUnit 4 is active. Wrapping the async lambdas with (Func<Task>) disambiguates unambiguously. This closes the last of the 9-file Bug A cast-fix pass surfaced on the prior integration/up-to-pr-249 build (158 errors, 112 CS0121). The other 6 branches carry (Action) casts on sync sites in their own tests.
ottobolyos
added a commit
to ottobolyos/mtconnect.net
that referenced
this pull request
Aug 21, 2026
…r-239 # Conflicts: # tests/MTConnect.NET-Common-Tests/V2_6_V2_7/V2_7DataItemTypeTests.cs # tests/MTConnect.NET-JSON-cppagent-Tests/Streams/JsonConditionsArrayShapeTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Upgrade every test project from NUnit 3.14.0 to NUnit 4.6.1 — the current stable line — and normalize the fragmented NUnit / NUnit3TestAdapter versions across the test tree that had drifted onto two different baselines.
Motivation
NUnit 3's lifetime is now maintenance-only; NUnit 4 is the active stable line and receives new features, .NET 8/9 hardening, and framework-level fixes. Four of the nine test csprojs still sat on NUnit 3.13.3 with NUnit3TestAdapter 4.3.1/4.5.0, while the other five had already moved to 3.14.0 + 4.6.0 — a fragmented baseline that risked masking version-specific behavior. Bringing everything onto NUnit 4.6.1 clears the drift, aligns the runner surface (relevant for the upcoming Stryker.NET adoption in a follow-up PR), and gets ahead of NUnit 3's deprecation curve.
Approach
Two commits, in the shape [normalize] then [bump]:
chore(tests): normalize NUnit and NUnit3TestAdapter versions across tests— mechanical version alignment across the four lagging csprojs (MTConnect-Compliance-Tests,MTConnect.NET-AgentModule-MqttRelay-Tests,MTConnect.NET-JSON-Tests,MTConnect.NET-JSON-cppagent-Tests) so all nine sit on the highest currently-in-use pair before the framework bump. Zero source changes; behavior identical.chore(tests): upgrade NUnit 3.14.0 to 4.6.1 across all test projects— every test csproj moves to NUnit 4.6.1, and two compiler-driven source migrations follow:CS0121 delegate-overload ambiguity (84 sites, 20 files). NUnit 4 added
Action/Func<T>overloads alongside the legacyTestDelegate/ActualValueDelegateoverloads onAssert.DoesNotThrow,Assert.Throws<T>,Assert.ThrowsAsync<T>,Assert.Multiple, andAssert.That. Lambda arguments are now ambiguous under overload resolution, so each affected site casts the lambda to(Action)to unambiguously bind to the newActionoverload.Assert.Thatsites are cast only when the constraint is aThrows.*form (void-lambda variant); the value-returningIs.*form is not affected in this corpus. Where the(Action)cast introduced a bareActionreference in files that only transitively importedSystem,using System;was added.CS9202
Assert.AreEqualextension-method dispatch (3 sites, 2 files). NUnit 4 removed the classic staticAssert.AreEqualand replaced it with an extension-method shim onAssertexposed byNUnit.Framework.Legacy. The extension-method shim requires C# 14 (featureextensions), which the test projects do not target. The safe migration isClassicAssert.AreEqualfromNUnit.Framework.Legacy— a plain static method that works under every C# language version. Two files (AgentUuidDeterministicDefaultTests.cs,XmlStreamsResponseDocumentTests.cs) switch the callers and addusing NUnit.Framework.Legacy;.NUnit3TestAdapterstays at 4.6.0 — the adapter version tracks the adapter's own release line, not the framework version, and 4.6.0 supports both NUnit 3 and NUnit 4 discovery.Depends on
(no in-flight PR dependencies — merges cleanly against master.)
Dime review cycle 1
Six-agent sweep (code-review, security-audit, simplification, improvement, documentation-audit, test-coverage-audit) at head
3a8954cd. All MEDIUM+ findings closed atomically or tracked with an issue number.Atomic fix landed on
5e205e3a— swap the 3ClassicAssert.AreEqualsites toAssert.That(actual, Is.EqualTo(expected))and drop bothusing NUnit.Framework.Legacy;imports:[F-CR-001]MEDIUM (code-review) — same recommendation.[F-IMP-002]MEDIUM (improvement) — same recommendation.[F-SIMP-001]LOW (simplification,AgentUuidDeterministicDefaultTests.cs) — same recommendation.[F-SIMP-002]LOW (simplification,XmlStreamsResponseDocumentTests.cs) — same recommendation.Four agents converged on the same fix —
ClassicAssert.AreEqualis NUnit's own "temporary expedience", constraint-basedAssert.Thatreads uniformly with every other assertion in the diff, and theLegacyimport is no longer needed. Full-solution build stays at 0 warnings, 0 errors; the two focused suites (AgentUuidDeterministic*,XmlStreamsResponseDocument*) stay green after the swap.MEDIUM tracked via new issue #240:
[F-IMP-001]MEDIUM (improvement) — adoptDirectory.Packages.props(Central Package Management). All 9 test csprojs now duplicateNUnit@4.6.1,NUnit3TestAdapter@4.6.0,Microsoft.NET.Test.Sdk@17.14.1,coverlet.collector@6.0.4— the first drift will be silent (and chore(tests): upgrade NUnit 3 to 4.6.1 across all test projects #239 itself was born from precisely that pattern). Sequenced before the follow-up Stryker.NET adoption PR.LOW SKIP-rationale — minimal-diff justifies the mechanical shape; constraint-model migration is legitimate follow-up scope:
[F-CR-002]— convert 71(Action)(() => …)casts onAssert.DoesNotThrow/Throws<T>/That(delegate, Throws.…)toAssert.That(() => …, Throws.Nothing)/Throws.TypeOf<T>().[F-CR-004]— same tradeoff onJsonSampleValueToObservationTests.csandJsonSampleValueConverterEdgeCaseTests.cs.[F-IMP-003]—(Action)→(TestDelegate)sed sweep. Style call; either reads well.LOW TRACK — follow-up-scoped, not blocking:
[F-CR-003]— replaceAssert.Multiple((Action)(() => { … }))blocks withusing (Assert.EnterMultipleScope()) { … }(NUnit 4.2+). Cast-free and reads as ambient scope; will be flagged as a nice cleanup in a subsequent quality-pass PR.[F-IMP-004]— benchmark[assembly: Parallelizable(ParallelScope.Fixtures)]per test project; suites that bind to fixed HTTP/MQTT ports need per-project audit before flipping. Land under a dedicated performance chore.CLOSE — confirmations only, no code change:
[F-IMP-005]— Stryker.NET follow-up unblocked (noAssert.Throwsweakening in diff).[F-SEC-001, F-SEC-002, F-SEC-003]— no CVE surface introduced; everyAssert.Throwsdiff hunk preserves the lambda body verbatim;XmlReaderSettingsnote is pre-existing pattern outside PR scope.[F-TEST-001, F-TEST-002, F-TEST-003, F-TEST-004]— behavior parity spot-checked across all 20 files;Is.EquivalentTosites stay on same-type collections; zero test smells introduced.[F-TEST-005]— Docs-Tests bluefin-flake escape hatch already exists (--filter "Category!=E2E");RouteCheckHelpersTests.csis deliberately Category-free and runs in either mode.Documentation audit — CLEAN. No
///doc regression on the 25 touched.csfiles; no stale NUnit 3 references indocs/**, root markdown,.github/workflows/*.yml, or CHANGELOG (none exists);MTConnect.NET-Tests-Agents(fixture-only, no NUnit ref) is not referenced anywhere that would become inconsistent.Head after cycle 1:
5e205e3a6371feca93b9782e71dd4dce7d61bc79. Integration branchintegration/up-to-pr-239atcfbf89448e221233a157e4dd9098f22995d85fd7.