Powershell 可以使用Pester和/或Mock测试.ps1脚本吗?
考虑一个.ps1脚本,它接受参数并进行状态更改。我想测试脚本的两个方面:Powershell 可以使用Pester和/或Mock测试.ps1脚本吗?,powershell,pester,Powershell,Pester,考虑一个.ps1脚本,它接受参数并进行状态更改。我想测试脚本的两个方面: 从调用者处接收参数,键入参数,并根据期望将其排序为参数集 正确进行了关键内部函数调用 这一点,考虑这个脚本,StaseCudioDimo.PS1: param([object[]]$stateChangeRequests) function ChangeSystemStateSomehow($changeRequestParameter) { # ... apply $cha
- 从调用者处接收参数,键入参数,并根据期望将其排序为参数集
- 正确进行了关键内部函数调用
这一点,考虑这个脚本,StaseCudioDimo.PS1:
param([object[]]$stateChangeRequests)
function ChangeSystemStateSomehow($changeRequestParameter)
{
# ... apply $changeRequestParameter to system state
}
$transformedRequests = $stateChangeRequests | % {
# do some processing of requests
}
$transformedRequests | % {
ChangeSystemStateSomehow $_
}
是否可以使用纠缠和嘲弄来:
$stateChangeRequests
的值,根据调用方式验证它是否具有预期的值和形状changesystemstate
的调用,以确保a)函数在测试过程中不会造成损坏(尤其是由于缺陷),以及b)函数以预期值被调用注意:我可以看到将脚本的各个部分移动到模块中有助于测试,但是对于这个问题,让我们假设脚本中的所有内容都需要保留在脚本中 经过一些实验,我终于找到了一些令人满意的答案。在大多数情况下,答案是肯定的,但有一点诡计 下面是测试示例,但以下是我开发测试所针对的StateChangingDemo.ps1的内容:
[CmdletBinding(DefaultParameterSetName='SetA')]
param(
[Parameter(ParameterSetName='SetA')]
[object[]]$TypeA_Requests,
[Parameter(ParameterSetName='SetB')]
[switch]$TypeB_All
)
function ChangeSystemStateSomehow($changeRequestParameter)
{
# ... apply $changeRequestParameter to system state
"system state was modified"
}
$transformedRequests = $TypeA_Requests | % {
# do some processing of requests
$_
}
$transformedRequests | % {
ChangeSystemStateSomehow $_
}
模拟.ps1内部的函数
如果.ps1调用需要模拟的函数(例如changesystemstate
),则在调用.ps1之前,需要存在该函数的一个版本并对其进行模拟
Describe 'test StateChangingDemo.ps1' {
Context 'no mock' {
It 'needs to be mocked' {
& .\StateChangingDemo.ps1 | Should -Be "system state was modified"
}
}
Context 'no mock, but placeholder function defined' {
function ChangeSystemStateSomehow {
"placeholder function"
}
# Show that a function in current scope won't displace the one in the script
It 'still needs to be mocked' {
& .\StateChangingDemo.ps1 | Should -Be "system state was modified"
}
}
Context 'function called by script is mocked' {
# This is required because Mock can't work before a function by the same name exists.
# However, the mock persists even though the function is later replaced.
function ChangeSystemStateSomehow {
"placeholder function"
}
Mock ChangeSystemStateSomehow {"fake execution"}
It 'can have functions within mocked' {
& .\StateChangingDemo.ps1 | Should -Be "fake execution"
}
}
}
在脚本上测试参数解析
测试脚本的这一方面得益于两种方法
应具有参数
第一种方法是认识到Should-HaveParameter
可以直接用于脚本:
Describe 'test StateChangingDemo.ps1' {
Context 'no mock again' {
It 'has expected parameters' {
Get-command .\StateChangingDemo.ps1 | Should -Not -BeNullOrEmpty
Get-command .\StateChangingDemo.ps1 | Should -HaveParameter TypeA_Requests -Type [object[]]
Get-command .\StateChangingDemo.ps1 | Should -HaveParameter TypeB_All -Type [switch]
}
}
}
第二种方法是模拟脚本本身。这并不是那么简单:
Context 'whole script mock attempted' {
Mock .\StateChangingDemo.ps1 {"fake script execution"}
It 'cannot be mocked directly' {
.\StateChangingDemo.ps1 | Should -Be "system state was modified"
}
}
上面通过的测试表明,即使我们建立了一个mock,脚本在被调用时仍然执行
模仿脚本本身
因此,我们创建了一个函数,其实现来自将脚本视为脚本块:
Context 'whole script wrapped' {
Set-Item function:fnStateChangingDemo ([ScriptBlock]::Create((get-content -Raw .\StateChangingDemo.ps1)))
It 'has expected parameters' {
Get-command fnStateChangingDemo | Should -Not -BeNullOrEmpty
Get-command fnStateChangingDemo | Should -HaveParameter TypeA_Requests -Type "object[]" -Because 'it has TypeA_Requests'
Get-command fnStateChangingDemo | Should -HaveParameter TypeB_All -Type "switch" -Because 'it has TypeB_All'
}
Mock ChangeSystemStateSomehow {"fake execution"}
It 'can still have functions within mocked when it is wrapped with a function' {
& fnStateChangingDemo | Should -Be "fake execution"
}
}
上面的测试表明,该函数呈现与脚本本身相同的接口,因此我们可以以相同的方式在其上测试参数。此外,我们现在可以使用mock安全地对参数解析做出更复杂的断言:
Context 'whole script wrapped and mocked' {
Set-Item function:fnStateChangingDemo ([ScriptBlock]::Create((get-content -Raw .\StateChangingDemo.ps1)))
Mock fnStateChangingDemo {"fake script function execution"}
It 'can be mocked when wrapped as function' {
fnStateChangingDemo | Should -Be "fake script function execution"
}
It 'cannot accept conflicting parameters' {
# Using multiple parameter sets is not allowed
{fnStateChangingDemo -TypeA_Requests 1,2,3 -TypeB_All} | Should -Throw "Parameter set cannot be resolved"
}
}
使用更复杂的模拟体、Assert MockCalled和Assert verifiablemock以及参数过滤器,我们可以使用模拟函数包装器来设计和测试脚本的用法,而不会冒测试要求它在意外情况下执行某些实际操作的风险