From 12da6b54b27b161a014b8515a63a083e0ce71c2a Mon Sep 17 00:00:00 2001 From: Jakub Jares Date: Thu, 25 Jun 2026 23:32:57 +0200 Subject: [PATCH] Fix #2143: keep NUnit2.5 mixed suite results valid Wrap plain test cases when an NUnit2.5 results node also contains child suites. This keeps the schema choice consistent for mixed ForEach and plain tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/functions/TestResults.NUnit25.ps1 | 38 +++++++++++++++++++++ tst/Pester.RSpec.TestResults.NUnit25.ts.ps1 | 23 +++++++++++++ 2 files changed, 61 insertions(+) diff --git a/src/functions/TestResults.NUnit25.ps1 b/src/functions/TestResults.NUnit25.ps1 index a0b8f74ff..9180c4fcd 100644 --- a/src/functions/TestResults.NUnit25.ps1 +++ b/src/functions/TestResults.NUnit25.ps1 @@ -100,11 +100,14 @@ function Write-NUnitTestSuiteElements { $XmlWriter.WriteStartElement('results') + $hasRunnableChildBlocks = $false foreach ($action in $Node.Blocks) { if (-not $action.ShouldRun) { # skip blocks that were discovered but did not run continue } + + $hasRunnableChildBlocks = $true Write-NUnitTestSuiteElements -Node $action -XmlWriter $XmlWriter -Path $action.ExpandedPath } @@ -114,6 +117,9 @@ function Write-NUnitTestSuiteElements { $Node.Tests | & $SafeCommands['Group-Object'] -Property GroupId | & $SafeCommands["Sort-Object"] -Property Name ) + $hasParameterizedTestSuites = 0 -lt @($suites | & $SafeCommands['Where-Object'] { $_.Name }).Count + $requiresTestCaseWrapper = $hasRunnableChildBlocks -or $hasParameterizedTestSuites + foreach ($suite in $suites) { # When group has name it is a parameterized tests (data-generated using -ForEach/TestCases) so we want extra level of nesting for them $testGroupId = $suite.Name @@ -133,8 +139,21 @@ function Write-NUnitTestSuiteElements { continue } + if (-not $testGroupId -and $requiresTestCaseWrapper) { + $testCaseSuiteInfo = Get-TestSuiteInfoForTestResult -TestResult $testCase + + $XmlWriter.WriteStartElement('test-suite') + Write-NUnitTestSuiteAttributes -TestSuiteInfo $testCaseSuiteInfo -XmlWriter $XmlWriter + $XmlWriter.WriteStartElement('results') + } + $suiteName = if ($testGroupId) { $parameterizedSuiteInfo.Name } else { '' } Write-NUnitTestCaseElement -TestResult $testCase -XmlWriter $XmlWriter -Path ($testCase.Path -join '.') -ParameterizedSuiteName $suiteName + + if (-not $testGroupId -and $requiresTestCaseWrapper) { + $XmlWriter.WriteEndElement() + $XmlWriter.WriteEndElement() + } } if ($testGroupId) { @@ -186,6 +205,25 @@ function Get-ParameterizedTestSuiteInfo { return Get-TestSuiteInfo -TestSuite $node -Path $node.Path } +function Get-TestSuiteInfoForTestResult { + param($TestResult) + + $node = [PSCustomObject] @{ + Duration = $TestResult.Duration + FailedCount = 0 + SkippedCount = 0 + InconclusiveCount = 0 + } + + switch ($TestResult.Result) { + Failed { $node.FailedCount = 1; break } + Skipped { $node.SkippedCount = 1; break } + Inconclusive { $node.InconclusiveCount = 1; break } + } + + return Get-TestSuiteInfo -TestSuite $node -Path $TestResult.Path +} + function Get-TestSuiteInfo { param($TestSuite, $Path) # if (-not $Path) { diff --git a/tst/Pester.RSpec.TestResults.NUnit25.ts.ps1 b/tst/Pester.RSpec.TestResults.NUnit25.ts.ps1 index 9f5c6fbc8..1c3e7eacf 100644 --- a/tst/Pester.RSpec.TestResults.NUnit25.ts.ps1 +++ b/tst/Pester.RSpec.TestResults.NUnit25.ts.ps1 @@ -436,6 +436,29 @@ i -PassThru:$PassThru { $null = $xmlResult.Schemas.Add($null, $schemaPath) $xmlResult.Validate( { throw $args[1].Exception }) } + + t 'Should validate against the nunit 2.5 schema when ForEach and plain tests are mixed' { + # https://github.com/pester/Pester/issues/2143 + $sb = { + Describe 'Demonstrate NUnit Problem' { + It 'Should return when type is ' -ForEach @( + @{ Type = 'String'; Variable = [String]'Test'; Result = $true } + @{ Type = 'Int'; Variable = [Int]0; Result = $false } + ) { + ($Variable -is [String]) | Should -Be $Result + } + + It 'A plain non-parameterized test' { 1 | Should -Be 1 } + } + } + $r = Invoke-Pester -Configuration ([PesterConfiguration]@{ Run = @{ ScriptBlock = $sb; PassThru = $true }; Output = @{ Verbosity = 'None' } }) + + $xmlResult = $r | ConvertTo-NUnitReport + + # verify against schema + $null = $xmlResult.Schemas.Add($null, $schemaPath) + $xmlResult.Validate( { throw $args[1].Exception }) + } } b "Exporting multiple containers" {