skills/pester-migration/SKILL.md
Experimental (preview) Pester migration skill for upgrading PowerShell Pester test suites across major versions — v3→v4, v4→v5, and v5→v6. The v5→v6 path tracks Pester 6, which is still a release candidate, so that guidance may change. Covers the Discovery/Run two-phase model, moving setup into BeforeAll, $PSScriptRoot vs $MyInvocation, mock changes (Assert-MockCalled → Should -Invoke, removed fall-through), Invoke-Pester parameters → PesterConfiguration, data-driven -ForEach/-TestCases, and the v6 breaking changes. Use when the user asks to upgrade, migrate, or modernize Pester tests, fix *.Tests.ps1 files that broke after bumping the Pester version, or convert legacy Should / Invoke-Pester syntax.
npx skillsauth add williamlimasilva/.copilot pester-migrationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Experimental / preview. The v5→v6 guidance tracks Pester 6 while it is a release candidate and may change; verify against the current release notes. v3→v4 and v4→v5 cover stable releases.
Pester is the test framework for PowerShell. Test files end in *.Tests.ps1 and use
Describe / Context / It blocks with Should assertions. This skill upgrades an existing
suite from one major Pester version to the next and gets it green again.
Mental model: each major jump has a different character. v3→v4 is mostly a syntax rename. v4→v5 is a fundamental runtime change (the Discovery/Run split) and is the hard one. v5→v6 is largely backwards-compatible — a handful of previously-deprecated things now throw. Migrate one major at a time; never skip a version.
Detailed, symptom-driven guides live in references/ — load the one(s) for the jump you are doing.
| Reference | When to load |
|---|---|
| v3-to-v4.md | Should Be → Should -Be, Contain → FileContentMatch, Assert-VerifiableMocks → Assert-VerifiableMock, array-assertion edge cases. |
| v4-to-v5.md | The big one. Discovery/Run phases, BeforeAll setup, $PSScriptRoot, BeforeDiscovery, -ForEach, mock scoping, Should -Throw wildcards, Invoke-Pester → New-PesterConfiguration. |
| v5-to-v6.md | PowerShell 5.1/7.4+ only, per-file discovery+run, empty -ForEach throws, duplicate setup blocks throw, name <...> templates evaluate, Assert-MockCalled removed, mocks no longer fall through, code-coverage tracer, legacy Invoke-Pester params removed. |
Canonical source: the official migration guides at https://pester.dev/docs/migrations/ — this skill mirrors them. When in doubt, prefer the website.
Find the installed version(s) and the version the tests were written for. These can differ.
# Installed Pester version(s) on this machine
Get-Module Pester -ListAvailable | Select-Object Name, Version, Path
# Version currently imported in the session
(Get-Module Pester).Version
Tell the source version from the test code with these heuristics:
| You see in *.Tests.ps1 / build scripts | Suite was written for |
|---|---|
| Should Be / Should Contain (no dash) | v3 or earlier → start at v3-to-v4 |
| $MyInvocation.MyCommand.Path + dot-source at the top of the file; arbitrary code directly under Describe | v4 → v4-to-v5 |
| Assert-MockCalled, Assert-VerifiableMock, Set-ItResult -Pending | v4 / early-v5 (these are removed in v6) |
| Invoke-Pester -Script … -OutputFile … -CodeCoverage … (legacy params) | v4 invocation → map to config |
| BeforeAll { . $PSScriptRoot/… }, New-PesterConfiguration, Should -Invoke | already v5-style → v5-to-v6 |
Install the target version when ready:
# Latest stable v5 — pin the major so this keeps installing v5 even after v6 goes GA
Install-Module Pester -MaximumVersion 5.99.99 -Force
# Pester 6 (currently a release candidate — needs -AllowPrerelease)
Install-Module Pester -AllowPrerelease -Force
On Windows PowerShell 5.1 the OS ships a Microsoft-signed built-in Pester 3 that PowerShellGet won't overwrite with the differently-signed newer Pester — add
-SkipPublisherCheckthere to install side-by-side. Not needed on PowerShell 7+. See https://pester.dev/docs/introduction/installation.
Run this loop for each major jump. Do not jump two majors at once — go v4→v5, then v5→v6.
# Bare Invoke-Pester works on every major; exact parameters differ
# (v3/v4: -Script/-OutputFile; v5+/v6: -Path/-Output).
Invoke-Pester
Install-Module (Step 0), then re-import: Remove-Module Pester; Import-Module Pester (or start a fresh session).-Output Detailed; use -Output Diagnostic (v4→v5) or read the
explicit v6 error messages to locate problems. Match each failure to the symptom → fix
tables in the reference.| Jump | Difficulty | Nature |
|---|---|---|
| v3 → v4 | Low | Assertion-syntax rename (Should -Be). Largely script-automatable. |
| v4 → v5 | High | New two-phase runtime. Test structure changes: setup must move into BeforeAll, discovery-time code into BeforeDiscovery, file location via $PSScriptRoot. Not a pure find-replace. |
| v5 → v6 | Low–Medium | Backwards-compatible runtime; deprecated features now throw. Mostly small, targeted fixes. Your Should -Be assertions keep working unchanged. |
# 1. Move file import into BeforeAll, use $PSScriptRoot (NOT $MyInvocation.MyCommand.Path)
# BEFORE
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
. "$here\Get-Thing.ps1"
# AFTER
BeforeAll { . $PSScriptRoot/Get-Thing.ps1 }
# 2. Any code that DISCOVERS/generates tests must be in BeforeDiscovery
BeforeDiscovery { $cases = Get-Content $PSScriptRoot/cases.json | ConvertFrom-Json }
# 3. Should -Throw matches with -like wildcards, not .Contains
{ throw 'a long message' } | Should -Throw '*long*'
# 4. Invoke-Pester legacy params → New-PesterConfiguration (see reference for full map)
Full details, scoping rules, and the parameter→config table: references/v4-to-v5.md.
# 1. Mock assertions: removed verbs — rename (old -> new):
# Assert-MockCalled -> Should -Invoke
# Assert-VerifiableMock -> Should -InvokeVerifiable
Should -Invoke Get-Thing -Times 1 -Exactly
Should -InvokeVerifiable
# 2. Add a default mock — unmatched calls no longer run the real command
Mock Get-Thing { 'default' }
Mock Get-Thing -ParameterFilter { $Name -eq 'a' } -MockWith { 'a' }
# 3. Empty/$null -ForEach now throws; allow it only where empty is expected
Describe 'Optional' -ForEach $cases -AllowNullOrEmptyForEach { }
# 4. Combine duplicate BeforeAll/BeforeEach/AfterAll/AfterEach in the same block into one
Full breaking-change list with symptoms and fixes: references/v5-to-v6.md.
Should syntax and dot-sourcing, but always review the diff and re-run the
suite afterward. Never bulk-edit and commit unchecked.*.Tests.ps1 — preserve the original
encoding (UTF-8 vs ASCII) so you don't mangle non-ASCII test names.git bisect useful if a
migrated test goes red later.development
Anxiety-aware, evidence-driven collaboration for stalled or high-stakes work when a user says uncertainty, repeated setbacks, or lack of visible progress is causing significant anxiety or distress. Use immediately when explicitly invoked; when this fit is only inferred from the user's own account, ask permission before applying it. Preserve the user's ideal and turn grounded perspective-taking into persistent, bounded problem solving. Do not use to diagnose, provide therapy, manufacture certainty, or lower goals for reassurance.
development
Build, review, debug, package, and test Roslyn diagnostic analyzers, code fix providers, and incremental source generators. Use for DiagnosticAnalyzer, CodeFixProvider, IIncrementalGenerator, IOperation analysis, Microsoft.CodeAnalysis dependency pinning, Roslyn test harnesses, C#/VB tests, and analyzer NuGet packaging.
testing
Migrates a project that uses checked-in .designer.cs files behind .resx to using a source-generator instead
development
Polish any GitHub repository's surface — labels (emoji rating tiers, P0–P3 priority, impact severity), issue forms, PR template, CI workflows, CODEOWNERS, rulesets, docs. Repo meta & config only — no code logic touched. Use when creating a new repo or polishing an existing one.