PowerShell测试与模拟的全面指南
立即解锁
发布时间: 2025-08-14 00:55:02 阅读量: 26 订阅数: 24 AIGC 


精通PowerShell脚本编写:自动化与简化任务
### PowerShell 测试与模拟的全面指南
#### 1. 测试前的准备与基础概念
在进行测试时,`BeforeDiscovery` 非常有用,它能确保在 Pester 尝试发现要执行的测试时,定义测试所需的值已经就位。Pester 还提供了其他命名块,可在运行阶段的特定点执行代码,用于定义变量和设置测试条件,但应避免定义要执行的测试。
Pester 提供了几种可在测试执行前后执行操作的块:
- `BeforeAll`:在该块中的任何测试之前执行一次。
- `BeforeEach`:在每个 `It` 块之前执行。
- `AfterAll`:在该块中的所有测试之后执行一次。
- `AfterEach`:在每个 `It` 块之后执行。
这些块在 `Describe` 或 `Context` 块中只能存在一次。如果 `Describe` 块包含 `BeforeAll`,嵌套的 `Context` 也包含 `BeforeAll`,则两个块都会执行(先执行 `Describe` 实例,再执行 `Context` 实例)。`BeforeAll` 和 `BeforeEach` 块在单元测试中定义命令的模拟时经常使用。
#### 2. 模拟命令
模拟是减少一组测试范围的重要手段,是单元测试的关键部分。模拟允许用测试中定义的实现替换命令的实际实现。模拟命令使用 `Mock` 关键字创建,可在 `BeforeAll`、`BeforeEach` 或 `It` 中使用。
在模拟时,上下文很重要。模拟必须在正确的作用域中创建才能生效。测试脚本时无需特定操作,但测试模块时必须使用 `ModuleName` 参数,该参数描述了要创建模拟的作用域,而非拥有被模拟命令的模块。
以下是一个示例函数,用于根据 CSV 文件中的状态启动或停止服务:
```powershell
@'
function Set-ServiceState {
[CmdletBinding()]
param (
[string]
$Path
)
Import-Csv $Path | ForEach-Object {
$service = Get-Service $_.Name
if ($service.Status -ne $_.ExpectedStatus) {
if ($_.ExpectedStatus -eq 'Stopped') {
Stop-Service -Name $_.Name
} else {
Start-Service -Name $_.Name
}
}
}
}
'@ | Set-Content -Path ServiceState.psm1
```
为了有效测试此命令,可模拟函数外部的每个命令。`Import-Csv` 和 `Get-Service` 需返回信息,`Start-Service` 和 `Stop-Service` 不返回任何内容。由于这是一个模块,使用 `Mock` 时需使用 `ModuleName` 参数:
```powershell
Mock Start-Service -ModuleName ServiceState
Mock Stop-Service -ModuleName ServiceState
```
`Import-Csv` 应输出包含 `Name` 和 `ExpectedStatus` 属性的对象,`Get-Service` 应返回包含 `Status` 属性的对象:
```powershell
Mock Import-Csv -ModuleName ServiceState {
[PSCustomObject]@{
Name = 'service1'
ExpectedStatus = 'Running'
}
}
Mock Get-Service -ModuleName ServiceState {
[PSCustomObject]@{
Status = 'Stopped'
}
}
```
可使用 `Should -Invoke` 断言测试模拟的执行:
```powershell
@'
BeforeDiscovery {
Import-Module .\ServiceState.psm1 -Force
}
Describe Set-ServiceState {
BeforeAll {
$module = @{ ModuleName = 'ServiceState' }
Mock Get-Service @module {
[PSCustomObject]@{
Status = 'Stopped'
}
}
Mock Import-Csv @module {
[PSCustomObject]@{
Name = 'service1'
ExpectedStatus = 'Running'
}
}
Mock Start-Service @module
Mock Stop-Service @module
}
It 'When ExpectedStatus is running, starts the service' {
Set-ServiceState -Path file.csv
Should -Invoke Start-Service @module
}
}
'@ | Set-Content ServiceState.tests.ps1
```
执行测试:
```powershell
PS> Invoke-Pester -Path .\ServiceState.tests.ps1
```
#### 3. 函数的不同执行路径
该函数中每个服务有三种可能的执行路径:
| 服务状态 | 期望状态 | 应执行的操作 |
| ---- | ---- | ---- |
| 与期望状态相同 | - | `Start-Service` 和 `Stop-Service` 不应运行 |
| 停止 | 运行 | `Start-Service` 应运行 |
| 运行 | 停止 | `Stop-Service` 应运行 |
#### 4. 参数变量与过滤
传递给模拟的参数值会绑定到变量,可在 `Mock` 主体中使用。例如:
```powershell
Mock Get-Service @module {
[PSCustomObject]@{
Name = $Name
Status = 'Stopped'
}
}
```
由于 `$PSBoundParameters` 在 `Mock` 中不能有效使用,Pester 提供了 `$PesterBoundParameters`:
```powershell
Mock Get-Service @module {
if ($PesterBoundParameters.ContainsKey('Name')) {
[PSCustomObject]@{
Name = $Name
Status = 'Stopped'
}
}
}
```
参数过滤器可用于定义何时使用特定模拟或断言模拟是否使用特定参数调用。可扩展 `Import-Csv` 的模拟,并为 `Get-Service` 创建多个模拟:
```powershell
Mock Import-Csv @module {
[PSCustomObject]@{
Name = 'service1'
ExpectedStatus = 'Running'
}
[PSCustomObject]@{
Name = 'service2'
ExpectedStatus = 'Running'
}
[PSCustomObject]@{
Name = 'service3'
ExpectedStatus = 'Stopped'
}
}
Mock Get-Service @module -ParameterFilter {
$Name -eq 'service1'
} {
[PSCustomObject]@{
Status = 'Running'
}
}
Mock Get-Service @module -ParameterFilter {
$Nam
```
0
0
复制全文
相关推荐










