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-43description: 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<!-- Generated from harness/github-copilot/plugins/testing-frameworks/skills/pester-migration/SKILL.md by harness/claude-code/scripts/convert_from_copilot.py. Edit the source, not this file. -->78# Pester migration910Upgrade 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.1112## When to invoke1314- "Migrate these Pester tests from v4 to v5."15- "Fix *.Tests.ps1 files that broke after upgrading Pester."16- "Convert legacy Invoke-Pester parameters to PesterConfiguration."17- "Why did my mocks stop working in Pester 6?"18- "Modernize this PowerShell test suite across Pester major versions."1920## Prerequisites and context2122- Pester test files usually end in `*.Tests.ps1` and use `Describe`, `Context`, `It`, and `Should`.23- Migrate one major jump at a time: v3→v4, then v4→v5, then v5→v6. Never skip a major version.24- Windows PowerShell 5.1 ships a Microsoft-signed built-in Pester 3; installing a newer module side-by-side may require `-SkipPublisherCheck`.25- Installation reference: https://pester.dev/docs/introduction/installation.2627## Progressive disclosure and bundled resources2829Load the reference for the exact jump before editing.3031| Reference | When to load |32| --- | --- |33| `references/v3-to-v4.md` | `Should Be` → `Should -Be`, `Contain` → `FileContentMatch`, `Assert-VerifiableMocks` → `Assert-VerifiableMock`, and array assertion edge cases. |34| `references/v4-to-v5.md` | Discovery/Run split, `BeforeAll`, `$PSScriptRoot`, `BeforeDiscovery`, `-ForEach`, mock scoping, `Should -Throw` wildcards, and `Invoke-Pester` → `New-PesterConfiguration`. |35| `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. |3637Canonical migration source: https://pester.dev/docs/migrations/v4-to-v5.3839## Version detection4041Run these before editing:4243```powershell44Get-Module Pester -ListAvailable | Select-Object Name, Version, Path45(Get-Module Pester).Version46```4748Infer the suite's source version from code, not only the installed module.4950| You see in tests or build scripts | Interpret as |51| --- | --- |52| `Should Be`, `Should Contain` without a dash | v3 or earlier; start with `references/v3-to-v4.md`. |53| `$MyInvocation.MyCommand.Path` and dot-sourcing at top level under `Describe` | v4; read `references/v4-to-v5.md`. |54| `Assert-MockCalled`, `Assert-VerifiableMock`, `Set-ItResult -Pending` | v4 or early v5; these are removed in v6. |55| `Invoke-Pester -Script ... -OutputFile ... -CodeCoverage ...` | Legacy invocation; map to config. |56| `BeforeAll { . $PSScriptRoot/... }`, `New-PesterConfiguration`, `Should -Invoke` | Already v5-style; assess v5→v6. |5758Install target versions deliberately:5960```powershell61Install-Module Pester -MaximumVersion 5.99.99 -Force62Install-Module Pester -Force63Remove-Module Pester; Import-Module Pester64```6566## Migration workflow67681. **Baseline**: run `Invoke-Pester` on the current version and record pass/fail so migration regressions are distinguishable from pre-existing failures.692. **Read the jump reference**: load only the reference file for the current major jump before editing.703. **Edit file by file**: apply mechanical changes and structural changes in small reviewable patches.714. **Switch Pester versions**: install/import the target major after source-compatible edits are complete.725. **Run with detail**: use `Invoke-Pester -Output Detailed`; for hard v4→v5 failures use `-Output Diagnostic` and match symptoms to the reference tables.736. **Fix to green**: rerun until results match the baseline or improve for documented reasons.747. **Review the diff**: keep a branch and commit per file or concern so `git bisect` remains useful.7576## Major-version cheat sheet7778| Jump | Difficulty | Nature | Key work |79| --- | --- | --- | --- |80| v3 → v4 | Low | Assertion syntax rename. | `Should -Be`, `FileContentMatch`, `Assert-VerifiableMock`, array assertion review. |81| v4 → v5 | High | Fundamental runtime change. | Move setup into `BeforeAll`; discovery-time generation into `BeforeDiscovery`; use `$PSScriptRoot`; migrate `Invoke-Pester` to config. |82| 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. |8384### v4 → v5 common fixes8586```powershell87# BEFORE88$here = Split-Path -Parent $MyInvocation.MyCommand.Path89. "$here\Get-Thing.ps1"9091# AFTER92BeforeAll { . $PSScriptRoot/Get-Thing.ps1 }9394BeforeDiscovery { $cases = Get-Content $PSScriptRoot/cases.json | ConvertFrom-Json }95{ throw 'a long message' } | Should -Throw '*long*'96```9798### v5 → v6 common fixes99100```powershell101Should -Invoke Get-Thing -Times 1 -Exactly102Should -InvokeVerifiable103Mock Get-Thing { 'default' }104Mock Get-Thing -ParameterFilter { $Name -eq 'a' } -MockWith { 'a' }105Describe 'Optional' -ForEach $cases -AllowNullOrEmptyForEach { }106```107108## Safety rules109110- **Tests are the spec**: do not change intended behavior unless a documented breaking change requires it and the user accepts the new behavior.111- **Automated scripts are helpers, not authority**: scripts can help with `Should` and dot-sourcing replacements but produce false positives.112- **Preserve encoding**: keep UTF-8 versus ASCII and non-ASCII test names intact when scripting over `*.Tests.ps1`.113- **Do not bulk-edit unchecked**: run the suite after each meaningful concern.114115## Compatibility terminology116117Preserve 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.118119- `-Output Detailed`120- `BeforeAll { . $PSScriptRoot/… }`121- `BeforeAll/BeforeEach/AfterAll/AfterEach`122- `DISCOVERS`123- `DISCOVERS/generates`124- `ForEach/-TestCases`125- `Install-Module`126- `Invoke-Pester -Script … -OutputFile … -CodeCoverage …`127- `Path/-Output`128- `Script/-OutputFile`129- `array-assertion`130- `backwards-compatible`131- `breaking-change`132- `data-driven`133- `differently-signed`134- `dot-source`135- `early-v5`136- `file/concern.**`137- `find-replace`138- `known-good`139- `pass/fail.`140- `passing/failing`141- `per-jump`142- `previously-deprecated`143- `re-import`144- `re-run`145- `script-automatable`146- `symptom-driven`147- `two-phase`148- `v3/v4`149150PowerShellGet note: on Windows PowerShell 5.1, PowerShellGet may require `-SkipPublisherCheck` for side-by-side Pester installation.151152## Output template153154```markdown155## Pester migration result156157**Status:** complete | needs fixes | blocked158**Source version:** <v3|v4|v5|unknown>159**Target version:** <v4|v5|v6>160161### Files changed162| File | Migration applied | Validation |163| --- | --- | --- |164| `*.Tests.ps1` | <syntax/setup/mock/config change> | <pass/fail evidence> |165166### Commands run167- `Get-Module Pester -ListAvailable | Select-Object Name, Version, Path`168- `Invoke-Pester <options>`169170### Remaining issues171- <failure, symptom, or human decision>172```173174## Quality gate175176- [ ] Source and target Pester versions were detected from both installed modules and test syntax.177- [ ] Only one major jump was migrated at a time.178- [ ] The relevant `references/` guide was loaded before edits.179- [ ] Baseline and final `Invoke-Pester` results were recorded.180- [ ] `BeforeAll`, `BeforeDiscovery`, `$PSScriptRoot`, mocks, `-ForEach`, and `New-PesterConfiguration` were handled when applicable.181- [ ] Test intent and file encoding were preserved.182183## References184185- [Pester v4 to v5 migration](https://pester.dev/docs/migrations/v4-to-v5)186- Installation: https://pester.dev/docs/introduction/installation.