PowerShell 仓库 Pester 测试指南:运行、编写与维护跨平台用例
【免费下载链接】PowerShellPowerShell for every system!项目地址: https://gitcode.com/GitHub_Trending/po/PowerShell
本指南以仓库 test/powershell/README.md 为骨架,系统讲解 PowerShell 仓库中 Pester 测试的执行方式、子进程启动规范、跨平台跳过策略与 Pending 约定。读者完成阅读后将掌握:用Start-PSPester运行与筛选整套 CI 测试、在用例内安全启动开发版pwsh子进程、用-Skip/-Pending正确表达"平台不适用"与"应当通过但暂未通过"两类状态,并把新用例放置到正确的测试目录。
PowerShell 仓库的 Pester 测试规模庞大(从引擎、语言解析器到各内置模块均有覆盖),这些用例同时运行于 Windows、Linux 与 macOS 的 CI 之上,因此对可移植性、进程隔离与状态表达有一套严格的约定。理解 Writing Pester Tests(由本 README 显式交叉引用)能进一步补齐编写侧的规范。
运行 Pester 测试:Start-PSPester 入口
仓库约定:先自行构建一份自托管(self-hosted)PowerShell,然后在仓库根目录、于该自托管 PowerShell 会话中执行Start-PSPester。
Import-Module ./build.psm1 Start-PSPesterStart-PSPester定义在 build.psm1,其默认行为是:启动刚构建好的pwsh进程,导入 Pester 模块,并对默认路径test/powershell下的用例执行Invoke-Pester,最终以 NUnit XML 格式写出结果文件(默认pester-tests.xml)。关键默认参数如下:
| 参数 | 默认值 | 说明 |
|---|---|---|
Path(位置 0) | test/powershell | 一个或多个测试文件/目录路径 |
Tag | CI,Feature | 只运行带这些标签的用例 |
ExcludeTag | Slow | 排除的标签(会按提权状态自动追加) |
OutputFormat | NUnitXml | Pester 输出格式,供 CI 消费 |
OutputFile | pester-tests.xml | XML 结果文件路径 |
Start-PSPester的Path参数还内置了针对*.tests.ps1的 Tab 补全(见 build.psm1 的ArgumentCompleter),可以快速从命令行定位目标测试文件。
限定运行范围
仓库文档中给出了两种限定方式:
- test/powershell/README.md 记载的写法是按名称模式过滤:
Start-PSPester -Tests SomeTestSuite*; - 根目录测试文档与当前 build.psm1 实现统一使用
Path(位置 0)参数指定目录或文件,例如 testing-guidelines.md 中的示例:
# 运行某个目录下的所有用例 Start-PSPester -Path test/powershell/engine/Api # 或只运行某一个测试文件 Start-PSPester -Path test/powershell/engine/Api/XmlAdapter.Tests.ps1在较新实现中优先采用-Path写法;-Path既接受目录,也接受具体.tests.ps1文件。
运行器在幕后做了什么
从 build.psm1 的实现可以看到,Start-PSPester会构造一条完整的启动命令并交给刚构建好的pwsh去执行,其中包括几项对测试正确性至关重要的动作:
- 设置遥测退出:以
$env:POWERSHELL_TELEMETRY_OPTOUT = 'yes'启动,避免测试受外部网络遥测行为干扰; - 注入测试模块路径:把临时测试模块目录前置到子进程的
PSModulePath,使测试用模块可被自动加载; - Windows 下调低执行策略:
Set-ExecutionPolicy -Scope Process Unrestricted,规避默认Restricted策略对测试脚本的拦截; - 按提权状态动态调整标签:非管理员 Windows 会话自动追加排除
RequireAdminOnWindows,Unix 非 sudo 会话自动追加排除RequireSudoOnUnix(见 build.psm1); - 始终携带
-noprofile启动(build.psm1),这正是下面一节要展开的规范。
在用例内启动新的 pwsh 子进程:-noprofile 与开发版定位
许多集成类测试需要再启动一个全新的powershell进程来验证命令行行为。此时必须遵守两条铁律:
- 必须带
-noprofile:用户的、系统的、被改动过的 profile 一旦被加载,会污染测试环境,导致用例在不该失败时失败。这正是上一节Start-PSPester自己也坚持用-noprofile启动子进程的原因——同一约定贯穿"运行器"与"用例"两个层面; - 必须调用开发版 PowerShell,而非 PATH 中的第一个:本机很可能同时装有正式发布版
pwsh,直接写pwsh会执行到错误解释器。正确做法是基于当前会话的$PsHome拼接出可执行文件路径——正在运行的正是开发版,从它的目录派生子进程就能保证测试的是当前构建。
README 给出的标准范例:
$powershell = Join-Path -Path $PsHome -ChildPath "pwsh" & $powershell -noprofile -command "ExampleCommand" | Should Be "ExampleOutput"注意 Windows 上实际的可执行文件名是pwsh.exe,而统一写成pwsh在两侧均可解析(Unix 无扩展名、Windows 上按扩展名自动补全);若你所在分支要求更严格的写法,可显式区分平台。
这一模式在真实用例中大量落地,例如 test/powershell/Host/Base-Directory.Tests.ps1:
& $powershell -noprofile -c `$PROFILE | Should -Be $expectedProfile & $powershell -noprofile -c `$env:PSModulePath & $powershell -noprofile { (Get-PSReadLineOption).HistorySavePath } | Should -Be $expectedReadline & $powershell -noprofile { exit }这些用例通过-noprofile干净地探测子进程在"零 profile 影响"下的$PROFILE、PSModulePath、PSReadLine 配置与退出行为——任何一条规则被破坏(例如落到系统已装版本的 pwsh),断言都会失真。此外,根目录测试文档 testing-guidelines.md 也提示:即便只是在本机运行测试,也应确保外层 PowerShell 以-noprofile启动,因为非默认环境可能导致部分用例失败。
可移植性:用 -Skip 表达"平台专属"
仓库测试需要跑在 Windows、Linux、macOS 三种平台上,因此存在大量"仅在某个平台有效"的用例。约定是:不要删除或改写它们,而是通过 Pester 的-Skip参数配合跨平台自动变量$IsWindows、$IsLinux、$IsMacOS在运行时决定是否执行。
仅在 Windows 上运行的写法:
It "Should do something on Windows" -Skip:($IsLinux -Or $IsMacOS) { ... }仅在 Linux / macOS 上运行的写法:
It "Should do something on Linux" -Skip:$IsWindows { ... }真实仓库中有大量同款实践。例如 Windows 专属的执行策略用例 test/powershell/Modules/Microsoft.PowerShell.Security/ExecutionPolicy.Tests.ps1:
It "Should return Microsoft.Powershell.ExecutionPolicy PSObject on Windows" -Skip:($IsLinux -Or $IsMacOS) { ... } It "Should succeed on Windows" -Skip:($IsLinux -Or $IsMacOS) { ... }文件系统层面的跨平台差异也会用同一手法处理,如 test/powershell/Modules/Microsoft.PowerShell.Management/Add-Content.Tests.ps1 中对不支持某 Provider 的平台显式跳过。
整块跳过:$PSDefaultParameterValues 技巧
当某个Describe内的用例"整块不适用于某平台"时,逐个加-Skip会显得啰嗦。WritingPesterTests.md 提供了基于$PSDefaultParameterValues的批量跳过方案:在BeforeAll中条件性地把It:skip置为$true,并在AfterAll中恢复原始值,从而使整个Describe(含其下所有Context/It)在非目标平台上报告为 Skipped 而非 Failed。这与逐条-Skip的语义一致,但更易维护。
与标签机制的分工
除了-Skip,仓库还通过Pester 标签管理"需要特殊权限"的用例(详见 WritingPesterTests.md 的 "Admin privileges in tests"):
- 需要 Windows 管理员权限的用例标记为
RequireAdminOnWindows; - 需要 Unix 下
sudo的用例标记为RequireSudoOnUnix(该标签优先于CI/Feature等其他标签)。
CI 会分两轮执行:常规轮次排除这些用例,专用轮次只运行它们。Start-PSPester在 build.psm1 中按当前会话是否提权自动向ExcludeTag追加对应标签,使"未提权的本地运行"与 CI 表现一致,也避免无权用例直接红掉。
Pending:标记"应当通过却暂未通过"的用例
与-Skip(平台不适用、不应运行)不同,仓库还约定了一种状态表达:测试本身写得没问题、理应通过,但因为某个未解决的缺陷暂时失败。此时既不能删除用例,也不要用-Skip悄悄跳过,而应使用 Pending:
It "Should Pass" -PendingPending 会在结果报告中以独立状态呈现,时刻提醒维护者该用例背后还有未关闭的缺陷;同时按 README 的约定,应当就阻塞原因在项目问题跟踪中登记一条 issue,保证"有人负责、可追踪"。
真实仓库中能看到两种 Pending 形态:
- 静态
-Pending:例如 test/powershell/Host/Base-Directory.Tests.ps1 的It "Can start in directory where name contains wildcard characters" -Pending; - 条件式与
Set-ItResult:例如 test/powershell/Host/ConsoleHost.Tests.ps1 中使用Set-ItResult -Pending -Because "..."在用例体内根据运行时事实动态置为 Pending,以及-Pending:($IsWindows)(ConsoleHost.Tests.ps1)把条件化的 Pending 与平台判断结合。
编写用例的基本规范速览
README 所链接的 WritingPesterTests.md 是仓库内编写用例的权威细则,以下是与本主题强相关的要点,供对照自查:
- 文件命名:
<描述性名称>.tests.ps1,例如XmlAdapter.Tests.ps1; - Describe 必须打标签:
Describe需在CI、Feature、Scenario三选一,未打标签会被构建过程直接判失败。CI(单元级、秒级完成)、Feature(定期跑的功能级)、Scenario(不定期跑的跨功能集成级)三者层层放大范围; - 断言风格:基础值断言用
Should -Be,类型检查用Should -BeOfType System.Int32;预期报错用Should -Throw -ErrorId(比消息更稳,不随文化/语言变化),需要深入检查 ErrorRecord 成员时配合-PassThru取回错误对象; - 作用域结构:
Describe内定义的Mock与TestDrive:内容随块退出而清理,Context是更细的分组层级,BeforeAll/BeforeEach/AfterEach/AfterAll用于搭建与拆除,避免在Describe中散落"自由代码"(其执行时机极易被误解); - 文件隔离:涉及文件操作时一律使用 Pester 内置的
TestDrive:(即$TestDrive),测试结束后由 Pester 自动清空,避免对仓库与用户目录产生副作用; - 测试驱动数据:多组输入输出用
It ... -TestCases $testCases驱动,配合描述性用例名; - 跨平台纪律:避免依赖注册表与 COM,避免断言平台间天然变化的资源计数(如加载的格式化文件数量),多行字符串比较需先规范化行尾(Windows 为
\r\n,Unix 为\n,且受 clone 的 git 配置影响)。
测试目录布局:新用例放对位置
按 testing-guidelines.md 的"功能化布局"约定,Pester 测试统一位于 test/powershell 下:
- engine:引擎级测试,其下细分为 Api、Basic、ETS(扩展类型系统)、Help、Logging、Module、ParameterBinding、Remoting 等子目录;
- Host:控制台宿主相关(含 TabCompletion、Base-Directory、ConsoleHost 等用例,前述子进程启动示例正来自此处);
- Language:语言与解析相关;
- Modules:按内置模块组织(如
Microsoft.PowerShell.Security、Microsoft.PowerShell.Management等),修某模块 cmdlet 时,用例应放进对应模块目录; - Provider、SDK、dsc 等:分别对应 Provider、托管 SDK 与 DSC 场景。
此外,构建/CI 运行器 build.psm1 还支持通过-IncludeFailingTest、-IncludeCommonTests额外引入tools/failingTests与test/common下的用例,方便把"已知失败清单"与通用用例一并纳入本地验证。
小结
遵循上述约定即可获得"本地即 CI"的一致体验:用Start-PSPester(必要时以-Path收窄范围)跑整套或局部用例;在测试内部派生新pwsh时坚持从$PsHome定位开发版并强制-noprofile;用-Skip配合$IsWindows/$IsLinux/$IsMacOS表达平台差异,用RequireAdminOnWindows/RequireSudoOnUnix标签表达权限差异,用-Pending诚实记录尚未修复的缺陷并关联 issue;最后把新用例按功能放进 test/powershell 对应的子目录,并为每个Describe打上CI/Feature/Scenario标签。这套方法论既是仓库内部 CI 的根基,也是为 PowerShell 这类跨平台语言运行时贡献测试时最值得复用的工程范式。
【免费下载链接】PowerShellPowerShell for every system!项目地址: https://gitcode.com/GitHub_Trending/po/PowerShell
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考