You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When TRX reporting is enabled, ObjectModelConverters.ToTestNode throws InvalidOperationException for any NUnit test in a parameterized fixture declared in the global namespace. The exception is swallowed by the adapter's event handler, so the affected test results are silently dropped from both the TRX file and the run summary counts.
Critically, this includes failing tests: a failing test in such a fixture is discarded, failed: 0 is reported, and the process exits 0. A red build reports green.
Repro
Tests.cs:
usingNUnit.Framework;[TestFixture("1")]publicclassGlobalFixture(stringarg1){[Test]publicvoidThisTestFails()=>Assert.Fail("I FAILED - this should turn the build red");}
Error processing ThisTestFails event for GlobalFixture("1").ThisTestFails
System.InvalidOperationException: Unable to parse fully qualified type name from test case: GlobalFixture("1").ThisTestFails
at Microsoft.Testing.Extensions.VSTestBridge.ObjectModel.ObjectModelConverters.ToTestNode(...) in /_/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs:line 157
at Microsoft.Testing.Extensions.VSTestBridge.ObjectModel.FrameworkHandlerAdapter.RecordResult(TestResult testResult) in /_/src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/FrameworkHandlerAdapter.cs:line 136
at NUnit.VisualStudio.TestAdapter.NUnitEventListener.TestFinished(...)
at NUnit.VisualStudio.TestAdapter.NUnitEventListener.OnTestEvent(String report)
Test run summary: Zero tests ran
total: 0
failed: 0
succeeded: 0
skipped: 0
Process exit code: 0.
The failing test is reported nowhere. NUnit discovered and ran it (discovered 1 of 1), and it failed, but the result never reaches the summary or the TRX.
Expected
The run reports failed: 1 and exits non-zero. At minimum, an unparseable name on a reporting path should degrade gracefully rather than discard the result.
Two conditions are required
1. TRX must be enabled. Same project without --report-trx:
total: 2 (both tests counted)
with --report-trx:
total: 1 (global-namespace fixture's test silently dropped)
2. The fixture must be in the global namespace. Adding a namespace avoids it:
namespaceSome.Namespace{[TestFixture("1")]publicclassNamespacedFixture(stringarg1){[Test]publicvoidSimple()=>Assert.Pass(arg1);// works fine}}
Some.Namespace.NamespacedFixture("1").Simple — a . exists before the first (, parse succeeds.
GlobalFixture("1").Simple — first ( is at index 14 with no preceding ., parse fails.
Analysis
In ObjectModelConverters.ToTestNode, the throw sits inside the isTrxEnabled block, after the TestMethodIdentifierProperty and TryParseFullyQualifiedType fallbacks:
if(isTrxEnabled){if(testMethodIdentifierPropertyis not null){/* ... */}elseif(TryParseFullyQualifiedType(...)){/* ... */}else{thrownewInvalidOperationException("Unable to parse fully qualified type name...");}}
TryParseFullyQualifiedType assumes a Namespace.Type.Method shape and looks for a . before the first (. NUnit emits parameterized-fixture names as Fixture("arg").Method, which has no such . when the fixture is in the global namespace.
Two things seem worth separating:
The throw itself. This is a reporting-only concern with an existing non-throwing fallback path, so throwing — and having a caller swallow it, losing the result — seems disproportionate. Discarding a failed result and exiting 0 is the part that worries me most.
The parse.[perf-improver] perf: eliminate eager testFullName allocation in MSTestTestNodeConverter #9823 proposes removing TryParseFullyQualifiedType in favour of using FullClassName directly. That's framed as a perf change, but it looks like it would also remove this failure mode. If that lands, this may resolve incidentally — though the swallow-and-drop behaviour in FrameworkHandlerAdapter.RecordResult would remain as a general hazard.
I appreciate the NUnit FQN format is awkward to consume (see nunit/nunit3-vs-adapter#404), and a TestMethodIdentifierProperty from the NUnit side would sidestep the parse entirely. Filing here because the silent loss of a failing result seems worth addressing on this side regardless of what NUnit emits.
Versions
Package
Version
NUnit
4.6.1
NUnit3TestAdapter
6.2.0
Microsoft.NET.Test.Sdk
17.14.1
Microsoft.Testing.Extensions.TrxReport
2.3.3
TFM
net8.0 (also repros on net11.0, net48)
Originally surfaced in VerifyTests/Verify CI, where ~23 test results across parameterized fixtures were dropped from the uploaded TRX without the build going red.
Summary
When TRX reporting is enabled,
ObjectModelConverters.ToTestNodethrowsInvalidOperationExceptionfor any NUnit test in a parameterized fixture declared in the global namespace. The exception is swallowed by the adapter's event handler, so the affected test results are silently dropped from both the TRX file and the run summary counts.Critically, this includes failing tests: a failing test in such a fixture is discarded,
failed: 0is reported, and the process exits0. A red build reports green.Repro
Tests.cs:trxrepro.csproj:Run:
Actual
Process exit code:
0.The failing test is reported nowhere. NUnit discovered and ran it (
discovered 1 of 1), and it failed, but the result never reaches the summary or the TRX.Expected
The run reports
failed: 1and exits non-zero. At minimum, an unparseable name on a reporting path should degrade gracefully rather than discard the result.Two conditions are required
1. TRX must be enabled. Same project without
--report-trx:with
--report-trx:2. The fixture must be in the global namespace. Adding a namespace avoids it:
Some.Namespace.NamespacedFixture("1").Simple— a.exists before the first(, parse succeeds.GlobalFixture("1").Simple— first(is at index 14 with no preceding., parse fails.Analysis
In
ObjectModelConverters.ToTestNode, the throw sits inside theisTrxEnabledblock, after theTestMethodIdentifierPropertyandTryParseFullyQualifiedTypefallbacks:TryParseFullyQualifiedTypeassumes aNamespace.Type.Methodshape and looks for a.before the first(. NUnit emits parameterized-fixture names asFixture("arg").Method, which has no such.when the fixture is in the global namespace.Two things seem worth separating:
0is the part that worries me most.TryParseFullyQualifiedTypein favour of usingFullClassNamedirectly. That's framed as a perf change, but it looks like it would also remove this failure mode. If that lands, this may resolve incidentally — though the swallow-and-drop behaviour inFrameworkHandlerAdapter.RecordResultwould remain as a general hazard.I appreciate the NUnit FQN format is awkward to consume (see nunit/nunit3-vs-adapter#404), and a
TestMethodIdentifierPropertyfrom the NUnit side would sidestep the parse entirely. Filing here because the silent loss of a failing result seems worth addressing on this side regardless of what NUnit emits.Versions
NUnitNUnit3TestAdapterMicrosoft.NET.Test.SdkMicrosoft.Testing.Extensions.TrxReportOriginally surfaced in VerifyTests/Verify CI, where ~23 test results across parameterized fixtures were dropped from the uploaded TRX without the build going red.