Pester migration
Upgrade existing PowerShell Pester suites one major version at a time, using the correct migration guide for the source and target version, then rerun tests until the suite is green without changing what the tests assert.
When to invoke
- "Migrate these Pester tests from v4 to v5."
- "Fix *.Tests.ps1 files that broke after upgrading Pester."
- "Convert legacy Invoke-Pester parameters to PesterConfiguration."
- "Why did my mocks stop working in Pester 6?"
- "Modernize this PowerShell test suite across Pester major versions."
Prerequisites and context
- Pester test files usually end in
*.Tests.ps1 and use Describe, Context, It, and Should.
- Migrate one major jump at a time: v3→v4, then v4→v5, then v5→v6. Never skip a major version.
- Windows PowerShell 5.1 ships a Microsoft-signed built-in Pester 3; installing a newer module side-by-side may require
-SkipPublisherCheck.
- Installation reference: https://pester.dev/docs/introduction/installation.
Progressive disclosure and bundled resources
Load the reference for the exact jump before editing.
| Reference |
When to load |
references/v3-to-v4.md |
Should Be → Should -Be, Contain → FileContentMatch, Assert-VerifiableMocks → Assert-VerifiableMock, and array assertion edge cases. |
references/v4-to-v5.md |
Discovery/Run split, BeforeAll, $PSScriptRoot, BeforeDiscovery, -ForEach, mock scoping, Should -Throw wildcards, and Invoke-Pester → New-PesterConfiguration. |
references/v5-to-v6.md |
PowerShell 5.1/7.4+, per-file discovery+run, empty -ForEach, duplicate setup blocks, name <...> templates, Assert-MockCalled removal, mocks no longer fall through, code-coverage tracer, and legacy Invoke-Pester params removal. |
Canonical migration source: https://pester.dev/docs/migrations/v4-to-v5.
Version detection
Run these before editing:
Get-Module Pester -ListAvailable | Select-Object Name, Version, Path
(Get-Module Pester).Version
Infer the suite's source version from code, not only the installed module.
| You see in tests or build scripts |
Interpret as |
Should Be, Should Contain without a dash |
v3 or earlier; start with references/v3-to-v4.md. |
$MyInvocation.MyCommand.Path and dot-sourcing at top level under Describe |
v4; read references/v4-to-v5.md. |
Assert-MockCalled, Assert-VerifiableMock, Set-ItResult -Pending |
v4 or early v5; these are removed in v6. |
Invoke-Pester -Script ... -OutputFile ... -CodeCoverage ... |
Legacy invocation; map to config. |
BeforeAll { . $PSScriptRoot/... }, New-PesterConfiguration, Should -Invoke |
Already v5-style; assess v5→v6. |
Install target versions deliberately:
Install-Module Pester -MaximumVersion 5.99.99 -Force
Install-Module Pester -Force
Remove-Module Pester; Import-Module Pester
Migration workflow
- Baseline: run
Invoke-Pester on the current version and record pass/fail so migration regressions are distinguishable from pre-existing failures.
- Read the jump reference: load only the reference file for the current major jump before editing.
- Edit file by file: apply mechanical changes and structural changes in small reviewable patches.
- Switch Pester versions: install/import the target major after source-compatible edits are complete.
- Run with detail: use
Invoke-Pester -Output Detailed; for hard v4→v5 failures use -Output Diagnostic and match symptoms to the reference tables.
- Fix to green: rerun until results match the baseline or improve for documented reasons.
- Review the diff: keep a branch and commit per file or concern so
git bisect remains useful.
Major-version cheat sheet
| Jump |
Difficulty |
Nature |
Key work |
| v3 → v4 |
Low |
Assertion syntax rename. |
Should -Be, FileContentMatch, Assert-VerifiableMock, array assertion review. |
| v4 → v5 |
High |
Fundamental runtime change. |
Move setup into BeforeAll; discovery-time generation into BeforeDiscovery; use $PSScriptRoot; migrate Invoke-Pester to config. |
| v5 → v6 |
Low–Medium |
Deprecated features now throw. |
Replace removed mock verbs, handle empty -ForEach, merge duplicate setup blocks, account for no mock fall-through. |
v4 → v5 common fixes
# BEFORE
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
. "$here\Get-Thing.ps1"
# AFTER
BeforeAll { . $PSScriptRoot/Get-Thing.ps1 }
BeforeDiscovery { $cases = Get-Content $PSScriptRoot/cases.json | ConvertFrom-Json }
{ throw 'a long message' } | Should -Throw '*long*'
v5 → v6 common fixes
Should -Invoke Get-Thing -Times 1 -Exactly
Should -InvokeVerifiable
Mock Get-Thing { 'default' }
Mock Get-Thing -ParameterFilter { $Name -eq 'a' } -MockWith { 'a' }
Describe 'Optional' -ForEach $cases -AllowNullOrEmptyForEach { }
Safety rules
- Tests are the spec: do not change intended behavior unless a documented breaking change requires it and the user accepts the new behavior.
- Automated scripts are helpers, not authority: scripts can help with
Should and dot-sourcing replacements but produce false positives.
- Preserve encoding: keep UTF-8 versus ASCII and non-ASCII test names intact when scripting over
*.Tests.ps1.
- Do not bulk-edit unchecked: run the suite after each meaningful concern.
Compatibility terminology
Preserve these baseline terms when they appear in user input, existing files, logs, or migration output; they are included to keep legacy wording, commands, paths, and API names recognizable during execution.
-Output Detailed
BeforeAll { . $PSScriptRoot/… }
BeforeAll/BeforeEach/AfterAll/AfterEach
DISCOVERS
DISCOVERS/generates
ForEach/-TestCases
Install-Module
Invoke-Pester -Script … -OutputFile … -CodeCoverage …
Path/-Output
Script/-OutputFile
array-assertion
backwards-compatible
breaking-change
data-driven
differently-signed
dot-source
early-v5
file/concern.**
find-replace
known-good
pass/fail.
passing/failing
per-jump
previously-deprecated
re-import
re-run
script-automatable
symptom-driven
two-phase
v3/v4
PowerShellGet note: on Windows PowerShell 5.1, PowerShellGet may require -SkipPublisherCheck for side-by-side Pester installation.
Output template
## Pester migration result
**Status:** complete | needs fixes | blocked
**Source version:** <v3|v4|v5|unknown>
**Target version:** <v4|v5|v6>
### Files changed
| File | Migration applied | Validation |
| --- | --- | --- |
| `*.Tests.ps1` | <syntax/setup/mock/config change> | <pass/fail evidence> |
### Commands run
- `Get-Module Pester -ListAvailable | Select-Object Name, Version, Path`
- `Invoke-Pester <options>`
### Remaining issues
- <failure, symptom, or human decision>
Quality gate
References
1---2name: pester-migration-23description: Upgrade PowerShell Pester test suites across major versions v3 to v4, v4 to v5, and v5 to v6 while preserving test intent. Use when asked to migrate, modernize, or fix *.Tests.ps1 files after a Pester version bump, convert legacy Should or Invoke-Pester syntax, handle Discovery/Run failures, move setup into BeforeAll, migrate mocks, or adopt PesterConfiguration.4---56# Pester migration78Upgrade existing PowerShell Pester suites one major version at a time, using the correct migration guide for the source and target version, then rerun tests until the suite is green without changing what the tests assert.910## When to invoke1112- "Migrate these Pester tests from v4 to v5."13- "Fix *.Tests.ps1 files that broke after upgrading Pester."14- "Convert legacy Invoke-Pester parameters to PesterConfiguration."15- "Why did my mocks stop working in Pester 6?"16- "Modernize this PowerShell test suite across Pester major versions."1718## Prerequisites and context1920- Pester test files usually end in `*.Tests.ps1` and use `Describe`, `Context`, `It`, and `Should`.21- Migrate one major jump at a time: v3→v4, then v4→v5, then v5→v6. Never skip a major version.22- Windows PowerShell 5.1 ships a Microsoft-signed built-in Pester 3; installing a newer module side-by-side may require `-SkipPublisherCheck`.23- Installation reference: https://pester.dev/docs/introduction/installation.2425## Progressive disclosure and bundled resources2627Load the reference for the exact jump before editing.2829| Reference | When to load |30| --- | --- |31| `references/v3-to-v4.md` | `Should Be` → `Should -Be`, `Contain` → `FileContentMatch`, `Assert-VerifiableMocks` → `Assert-VerifiableMock`, and array assertion edge cases. |32| `references/v4-to-v5.md` | Discovery/Run split, `BeforeAll`, `$PSScriptRoot`, `BeforeDiscovery`, `-ForEach`, mock scoping, `Should -Throw` wildcards, and `Invoke-Pester` → `New-PesterConfiguration`. |33| `references/v5-to-v6.md` | PowerShell 5.1/7.4+, per-file discovery+run, empty `-ForEach`, duplicate setup blocks, name `<...>` templates, `Assert-MockCalled` removal, mocks no longer fall through, code-coverage tracer, and legacy `Invoke-Pester` params removal. |3435Canonical migration source: https://pester.dev/docs/migrations/v4-to-v5.3637## Version detection3839Run these before editing:4041```powershell42Get-Module Pester -ListAvailable | Select-Object Name, Version, Path43(Get-Module Pester).Version44```4546Infer the suite's source version from code, not only the installed module.4748| You see in tests or build scripts | Interpret as |49| --- | --- |50| `Should Be`, `Should Contain` without a dash | v3 or earlier; start with `references/v3-to-v4.md`. |51| `$MyInvocation.MyCommand.Path` and dot-sourcing at top level under `Describe` | v4; read `references/v4-to-v5.md`. |52| `Assert-MockCalled`, `Assert-VerifiableMock`, `Set-ItResult -Pending` | v4 or early v5; these are removed in v6. |53| `Invoke-Pester -Script ... -OutputFile ... -CodeCoverage ...` | Legacy invocation; map to config. |54| `BeforeAll { . $PSScriptRoot/... }`, `New-PesterConfiguration`, `Should -Invoke` | Already v5-style; assess v5→v6. |5556Install target versions deliberately:5758```powershell59Install-Module Pester -MaximumVersion 5.99.99 -Force60Install-Module Pester -Force61Remove-Module Pester; Import-Module Pester62```6364## Migration workflow65661. **Baseline**: run `Invoke-Pester` on the current version and record pass/fail so migration regressions are distinguishable from pre-existing failures.672. **Read the jump reference**: load only the reference file for the current major jump before editing.683. **Edit file by file**: apply mechanical changes and structural changes in small reviewable patches.694. **Switch Pester versions**: install/import the target major after source-compatible edits are complete.705. **Run with detail**: use `Invoke-Pester -Output Detailed`; for hard v4→v5 failures use `-Output Diagnostic` and match symptoms to the reference tables.716. **Fix to green**: rerun until results match the baseline or improve for documented reasons.727. **Review the diff**: keep a branch and commit per file or concern so `git bisect` remains useful.7374## Major-version cheat sheet7576| Jump | Difficulty | Nature | Key work |77| --- | --- | --- | --- |78| v3 → v4 | Low | Assertion syntax rename. | `Should -Be`, `FileContentMatch`, `Assert-VerifiableMock`, array assertion review. |79| v4 → v5 | High | Fundamental runtime change. | Move setup into `BeforeAll`; discovery-time generation into `BeforeDiscovery`; use `$PSScriptRoot`; migrate `Invoke-Pester` to config. |80| v5 → v6 | Low–Medium | Deprecated features now throw. | Replace removed mock verbs, handle empty `-ForEach`, merge duplicate setup blocks, account for no mock fall-through. |8182### v4 → v5 common fixes8384```powershell85# BEFORE86$here = Split-Path -Parent $MyInvocation.MyCommand.Path87. "$here\Get-Thing.ps1"8889# AFTER90BeforeAll { . $PSScriptRoot/Get-Thing.ps1 }9192BeforeDiscovery { $cases = Get-Content $PSScriptRoot/cases.json | ConvertFrom-Json }93{ throw 'a long message' } | Should -Throw '*long*'94```9596### v5 → v6 common fixes9798```powershell99Should -Invoke Get-Thing -Times 1 -Exactly100Should -InvokeVerifiable101Mock Get-Thing { 'default' }102Mock Get-Thing -ParameterFilter { $Name -eq 'a' } -MockWith { 'a' }103Describe 'Optional' -ForEach $cases -AllowNullOrEmptyForEach { }104```105106## Safety rules107108- **Tests are the spec**: do not change intended behavior unless a documented breaking change requires it and the user accepts the new behavior.109- **Automated scripts are helpers, not authority**: scripts can help with `Should` and dot-sourcing replacements but produce false positives.110- **Preserve encoding**: keep UTF-8 versus ASCII and non-ASCII test names intact when scripting over `*.Tests.ps1`.111- **Do not bulk-edit unchecked**: run the suite after each meaningful concern.112113## Compatibility terminology114115Preserve these baseline terms when they appear in user input, existing files, logs, or migration output; they are included to keep legacy wording, commands, paths, and API names recognizable during execution.116117- `-Output Detailed`118- `BeforeAll { . $PSScriptRoot/… }`119- `BeforeAll/BeforeEach/AfterAll/AfterEach`120- `DISCOVERS`121- `DISCOVERS/generates`122- `ForEach/-TestCases`123- `Install-Module`124- `Invoke-Pester -Script … -OutputFile … -CodeCoverage …`125- `Path/-Output`126- `Script/-OutputFile`127- `array-assertion`128- `backwards-compatible`129- `breaking-change`130- `data-driven`131- `differently-signed`132- `dot-source`133- `early-v5`134- `file/concern.**`135- `find-replace`136- `known-good`137- `pass/fail.`138- `passing/failing`139- `per-jump`140- `previously-deprecated`141- `re-import`142- `re-run`143- `script-automatable`144- `symptom-driven`145- `two-phase`146- `v3/v4`147148PowerShellGet note: on Windows PowerShell 5.1, PowerShellGet may require `-SkipPublisherCheck` for side-by-side Pester installation.149150## Output template151152```markdown153## Pester migration result154155**Status:** complete | needs fixes | blocked156**Source version:** <v3|v4|v5|unknown>157**Target version:** <v4|v5|v6>158159### Files changed160| File | Migration applied | Validation |161| --- | --- | --- |162| `*.Tests.ps1` | <syntax/setup/mock/config change> | <pass/fail evidence> |163164### Commands run165- `Get-Module Pester -ListAvailable | Select-Object Name, Version, Path`166- `Invoke-Pester <options>`167168### Remaining issues169- <failure, symptom, or human decision>170```171172## Quality gate173174- [ ] Source and target Pester versions were detected from both installed modules and test syntax.175- [ ] Only one major jump was migrated at a time.176- [ ] The relevant `references/` guide was loaded before edits.177- [ ] Baseline and final `Invoke-Pester` results were recorded.178- [ ] `BeforeAll`, `BeforeDiscovery`, `$PSScriptRoot`, mocks, `-ForEach`, and `New-PesterConfiguration` were handled when applicable.179- [ ] Test intent and file encoding were preserved.180181## References182183- [Pester v4 to v5 migration](https://pester.dev/docs/migrations/v4-to-v5)184- Installation: https://pester.dev/docs/introduction/installation.