Skip to content

Migrate Pester tests from v4 to v5#27290

Draft
nohwnd wants to merge 2 commits intoPowerShell:masterfrom
nohwnd:pester5-migration
Draft

Migrate Pester tests from v4 to v5#27290
nohwnd wants to merge 2 commits intoPowerShell:masterfrom
nohwnd:pester5-migration

Conversation

@nohwnd
Copy link
Copy Markdown
Contributor

@nohwnd nohwnd commented Apr 17, 2026

TL;DR

  • Mechanical migration from Pester 4.99 → 5.7.1 across 207 test files and 2 build/CI files.
  • No test logic, assertions, or expected behaviors were changed. Most edits move setup and data into BeforeDiscovery / BeforeAll blocks for Pester 5's execution model.
  • Test coverage is effectively retained (14,696 → 15,264 discovered tests). P5 actually discovers 568 more tests than P4 could. 3 of 4 P4 hangs resolved.

How to review: Most diffs are mechanical — look for the pattern of data moving into BeforeDiscovery {} and setup moving into BeforeAll {}. The two non-test files (build.psm1, tools/ci.psm1) contain the Pester version bump and invocation changes.


Why

Pester 4 is no longer maintained. This updates PowerShell's test infrastructure to the supported framework version and aligns the suite with Pester 5's discovery/execution model.


Before → After

Both baselines captured on Windows, same machine, same environment, 600s per-file timeout.

Metric Pester 4 Pester 5 Delta
Total tests 14,696 15,264 +568
Passed 13,254 12,952 −302
Failed 554 1,174 +620
Skipped 685 1,092 +407
Genuine hangs 4 1 −3
Errors 0 0 0

P5 runs 568 more tests and resolves 3 of 4 P4 hangs. The higher failed/skipped counts come from tests that P4 couldn't enumerate now being properly discovered — many are environment-dependent (remoting, services, admin rights) and correctly skip or fail. 342 files have identical test counts between P4 and P5.

Tests P4 couldn't discover (returned 0 or 1) — now working in P5
P4 P5 File Reason
0 124 LocalAccounts.LocalUser.Tests P4 returned 0 — test structure incompatible with P4 discovery
0 62 LocalAccounts.LocalGroup.Tests Same
0 43 LocalAccounts.LocalGroupMember.Tests Same
1 51 ConfigProvider.Tests P4 captured What if: output instead of result
1 50 Set-Service.Tests P4 returned 1
0 46 Import-Counter.Tests P4 returned 0
1 47 Copy-Item.Tests P4 TestDrive: reference issue
0 36 Test-Connection.Tests P4 hung (>600s), P5 completes in 6s
1 36 RemoteImportModule.Tests P4 returned 1
0 19 FileSignature.Tests P4 hung (>600s), P5 completes in 2s
1 20 RemoteGetModule.Tests P4 returned 1
1 20 TestWSMan.Tests P4 returned 1
0 14 Export-Counter.Tests P4 returned 0
0 6 Invoke-Item.Tests P4 hung (>600s), P5 completes in 3s
0 6 Get-EventLog.Tests P4 returned 0
Files where P5 has fewer tests (2 files, −62)
P4 P5 File Reason
79 43 DebuggerScriptTests.Tests.ps1 Dynamic Verify() function replaced with explicit It blocks and soft assertions. All breakpoint validation logic preserved.
26 0 Start-Process.Tests.ps1 Intermittent hang (~280s). Replaced ping with pwsh to avoid firewall popups.
Hang resolution
File P4 P5 Notes
FileSignature.Tests hang 19 tests, 2s Resolved
Test-Connection.Tests hang 36 tests, 6s Resolved
Invoke-Item.Tests hang 6 tests, 3s Resolved
Wait-Process.Tests hang hang Pre-existing: Wait-Process without -Timeout on ping

What changed

Build / CI (2 files)

File Change
build.psm1 Pester MaximumVersion 4.99RequiredVersion 5.7.1; Invoke-Pester rewritten to use [PesterConfiguration]
tools/ci.psm1 MaximumVersion 4.99MinimumVersion 5.0; same PesterConfiguration pattern

Test files (207 files)

The dominant migration pattern adapts to Pester 5's discovery/run phase separation:

Migration pattern example (click to expand)
# BEFORE (Pester 4) — data at script scope, available everywhere
$testCases = @(
    @{ Name = 'foo'; Expected = 1 }
)
Describe "Tests" {
    It "works with <Name>" -TestCases $testCases { ... }
}

# AFTER (Pester 5) — data in BeforeDiscovery, setup in BeforeAll
BeforeDiscovery {
    $testCases = @(
        @{ Name = 'foo'; Expected = 1 }
    )
}
Describe "Tests" {
    BeforeAll {
        # runtime setup that was previously at script scope
    }
    It "works with <Name>" -TestCases $testCases { ... }
}

Applied across 207 files:

Pattern Count Description
BeforeDiscovery blocks +260 Test-case data moved to discovery phase
BeforeAll blocks +236 Setup code moved from script scope
Setup removal 50 Replaced Setup -File/-Directory with New-Item/Set-Content targeting $TestDrive
returnSet-ItResult 14 Pester 5 treats return in It as silent pass; replaced with Set-ItResult -Skipped
try/catchShould -Throw 14 Simplified manual exception handling
try/finallyAfterAll 26 Restructured teardown patterns
$PSDefaultParameterValues['It:Skip']-Skip: P5 evaluates -Skip at discovery, not runtime
Duplicate variable cleanup 49 files Removed redundant script-scope + BeforeAll declarations
Hang fixes 4 Stricter execution model fixes

What did NOT change

  • ✅ All test assertions (Should -Be, Should -Throw, etc.) — unchanged
  • ✅ Test logic and expected behaviors — unchanged
  • ✅ No tests were deleted or force-skipped to make the migration pass
  • ✅ File structure and naming — unchanged
  • 342 files have identical test counts between P4 and P5

Scope by area

Area Files changed
Modules 111
Language 48
Engine 38
Host 5
Build/CI 2
Other (SSH, Provider, SDK, Test Tools) 5

Possible improvements (not in this PR)

  • Wait-Process.Tests: genuine hang in both versions. Adding -Timeout 30 to Wait-Process calls would fix it.
  • CompatiblePSEditions.Module.Tests: 107 failures from test environment lacking Windows PowerShell 5.1 WinCompat modules. Tests discover and run correctly.

Migrate the PowerShell test suite from Pester 4.99 to Pester 5.7.1.
207 test files updated for Pester 5's discovery/run execution model,
plus build.psm1 and tools/ci.psm1 for the version bump and
PesterConfiguration-based invocation.

No test logic, assertions, or expected behaviors were changed.

Key migration patterns applied across 207 test files:
- Add BeforeDiscovery blocks for test-case data (260 blocks)
- Move setup code from script scope into BeforeAll (236 blocks)
- Remove duplicate variable assignments (49 files)
- Fix test hangs from Pester 5 stricter execution (4 hangs resolved)
- Replace ping with pwsh in Start-Process.Tests (firewall popups)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@nohwnd nohwnd force-pushed the pester5-migration branch from c019447 to eeb6249 Compare April 17, 2026 19:34
@daxian-dbw daxian-dbw added the CL-Test Indicates that a PR should be marked as a test change in the Change Log label Apr 17, 2026
The PassThru property was added as a second Run key, causing
'Duplicate keys are not allowed in hash literals' error.
Merged PassThru into the single Run block.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@microsoft-github-policy-service microsoft-github-policy-service Bot added the Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept label Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-Test Indicates that a PR should be marked as a test change in the Change Log Waiting on Author The PR was reviewed and requires changes or comments from the author before being accept

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants