我遇到了一个实用函数:@Jaykul的Get-ParameterValues
,以及elovelan的稍微修改的版本。我选择使用不声明参数的elovelan版本。我可以确认,就我在命令行上进行的测试而言,该功能可以按预期工作。
我正在将该功能合并到我正在编写的私有模块中,作为通用实用程序功能(当然,要归功于 @jaykul 和 elovelan > !!),因此,我正在尝试为此编写单元测试。但是,在运行测试时,出现错误Cannot find a variable with the name 'PSBoundParameters'.
我觉得这与Pester无关,而与我对PowerShell的了解更多有关。我希望社区中的某人能够为我提供帮助。
Pester测试失败,错误为Cannot find a variable with the name 'PSBoundParameters'.
可以在here中找到被测函数。这是测试代码的一部分:
Feature: Get-ParameterValues
As a PowerShell function author
In order to more easily deal with function parameters
I want to be able to automatically populate a dictionary
containing the supplied function arguments or the parameter's
default value if the argument was not supplied
So that I can reduce boilerplate code when dealing with
function parameters.
Background: A test function with parameters
Given a test function
"""
function global:Test {
[CmdletBinding()]
param(
[Parameter(Position = 0)]
$ValueWithNoDefault,
[Parameter(Position = 1)]
$ValueWithDefault = 'default',
[Parameter(Position = 2)]
$ValueWithNullDefault = $null
)
$Parameters = (Get-ParameterValues)
$Parameters
}
"""
Scenario Outline: Get-ParameterValues returns a dictionary with the expected key and value
Given the value '<Value>' is provided for the '<Parameter>' parameter
When 'global:Test' is invoked
Then the hashtable '<ContainsAKey>' a key for the parameter
And the resolved parameter value is '<ExpectedValue>'.
Examples: Examples using parameter ValueWithNoDefault
| Parameter | Value | Contains | ExpectedValue |
| ValueWithNoDefault | <No value> | does not contain | <Does Not Exist> |
| ValueWithNoDefault | `$null | contains | `$null |
| ValueWithNoDefault | { `$foo = 1 } | contains | { `$foo = 1 } |
| ValueWithNoDefault | `$false | contains | `$false |
| ValueWithNoDefault | `$true | contains | `$true |
| ValueWithNoDefault | "" | contains | "" |
| ValueWithNoDefault | 0 | contains | 0 |
| ValueWithNoDefault | @() | contains | @() |
| ValueWithNoDefault | 1 | contains | 1 |
BeforeEachFeature {
Import-Module -Name "${pwd}\path-to\My-Module.psd1" -Scope Global -Force
filter global:ConvertFrom-TableCellValue {
Param(
[Parameter(Mandatory=$True, Position=0, ValueFromPipeline=$True)]
[AllowNull()]
[AllowEmptyString()]
$Value
)
Process {
if ($Value -eq '$null') {
$Value = $null
}
switch -Regex ($Value) {
'^"?\{\s*(?<ScriptBody>.*)\s*\}"?$' { $Value = [ScriptBlock]::Create($Matches.ScriptBody) }
'(?<StringValue>".*")' { $Value = ($Matches.StringValue -as [string]) }
'$?(?i:(?<Boolean>true|false))' { $Value = [Boolean]::Parse($Matches.Boolean) }
'^\d+$' { $Value = $Value -as [int] }
default { }
}
$Value
}
}
}
AfterEachFeature {
$Module = Get-Module -Name "My-Module"
if ($Module) {
$Module.Name | Remove-Module
}
if (Test-Path "Function:\global:ConvertTo-Parameter") {
Remove-Item Function:\global:ConvertTo-Parameter
}
}
Given "a test function" {
param ([string]$func)
Invoke-Expression $func
$f = Get-Item 'Function:\Test'
$f | Should -Not -BeNull
}
Given "the value '(?<Value>.*)' is provided for the '(?<Parameter>\S*)' parameter" {
param(
[object]$Value,
[string]$Parameter
)
$Value = ConvertFrom-TableCellValue $Value
if ($Value -ne "<No value>") {
$Context = @{ $Parameter = $Value }
} else {
$Context = @{}
}
}
When "'(?<CmdletName>.*)' is invoked" {
[CmdletBinding()]
Param ([string]$CmdletName)
# NOTE: I probably don't need the if block, but I added this during debugging of this issue...just-in-case.
# NOTE: The test fails in this step definition at this point when it executes the `Test` function which calls Get-ParameterValues.
if ($Context.Keys.Count -gt 0) {
$actual = &$CmdletName @Context
} else {
$actual = &$CmdletName
}
Write-Debug $actual.GetType().FullName
}
Then "the hashtable '(?<ContainsAKey>(does not contain|contains))' a key for the parameter" {
param([string]$ContainsAKey)
($Context.ContainsKey($Parameter) -and $actual.ContainsKey($Context.$Parameter)) | Should -Be ($ContainsAKey -eq 'contains')
}
psake version 4.7.4
Copyright (c) 2010-2017 James Kovacs & Contributors
Executing InstallDependencies
Executing Init
Executing Test
Testing all features in 'C:\src\git\My-Module\Specs\features' with tags: 'Get-ParameterValues'
Feature: Get-ParameterValues
As a PowerShell function author
In order to more easily deal with function parameters
I want to be able to automatically populate a dictionary
containing the supplied function arguments or the parameter's
default value if the argument was not supplied
So that I can reduce boilerplate code when dealing with
function parameters.
Scenario: Get-ParameterValues returns a dictionary with the expected key and value
Examples:Examples using parameter ValueWithNoDefault
[+] Given a test function 59ms
[+] Given the value '<No value>' is provided for the 'ValueWithNoDefault' parameter 187ms
[-] When 'global:Test' is invoked 48ms
at <ScriptBlock>, C:\src\git\My-Module\Public\Utility\Get-ParameterValues.ps1: line 74
74: $BoundParameters = Get-Variable -Scope 1 -Name PSBoundParameters -ValueOnly
From C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 35
Cannot find a variable with the name 'PSBoundParameters'.
[-] Then the hashtable 'does not contain' a key for the parameter 201ms
at <ScriptBlock>, C:\src\git\My-Module\Specs\features\steps\Utility\Get-ParameterValues.steps.ps1: line 38
38: ($Context.ContainsKey($Parameter) -and $actual.ContainsKey($Context.$Parameter)) | Should -Be ($ContainsAKey -eq 'contains')
From C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 36
You cannot call a method on a null-valued expression.
[?] And the resolved parameter value is '<Does Not Exist>'. 40ms
Could not find implementation for step!
At And, C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 37
有人可以告诉我为什么即使将参数传递到$PSBoundParameters
函数中,Get-ParameterValues
字典在Test
函数中也不存在吗? (我感觉这可能与PowerShell范围规则有关,因为When
脚本块具有“脚本”作用域-我试图通过确保已声明Test
函数来克服它在global
范围内。)
因此,要解决这个问题可能是我不了解的PowerShell作用域问题,我对Gherkin Given
步骤进行了如下修改:
Given a test function
"""
function global:Test {
[CmdletBinding()]
param(
[Parameter(Position = 0)]
$ValueWithNoDefault,
[Parameter(Position = 1)]
$ValueWithDefault = 'default',
[Parameter(Position = 2)]
$ValueWithNullDefault = $null
)
$DebugPreference = 'Continue'
if ($PSBoundParameters) {
Write-Debug "`$PSBoundParameters = $(ConvertTo-JSON $PSBoundParameters)"
} else {
Write-Debug "Unable to find `$PSBoundParameters automatic variable."
}
$Parameters = (Get-ParameterValues)
$Parameters
}
"""
这是针对前两个表条目运行测试的输出(请参见上面的原始功能文件):
psake version 4.7.4
Copyright (c) 2010-2017 James Kovacs & Contributors
Executing InstallDependencies
Executing Init
Executing Test
Testing all features in 'C:\src\git\My-Module\Specs\features' with tags: 'Get-ParameterValues'
Feature: Get-ParameterValues
As a PowerShell function author
In order to more easily deal with function parameters
I want to be able to automatically populate a dictionary
containing the supplied function arguments or the parameter's
default value if the argument was not supplied
So that I can reduce boilerplate code when dealing with
function parameters.
Scenario: Get-ParameterValues returns a dictionary with the expected key and value
Examples:Examples using parameter ValueWithNoDefault
[+] Given a test function 54ms
DEBUG: $Parameter: ValueWithNoDefault
DEBUG: $Value: <No value>
[+] Given the value '<No value>' is provided for the 'ValueWithNoDefault'
parameter 29ms
DEBUG: Cmdlet: Test
DEBUG: Context: {
}
DEBUG: $PSBoundParameters = {
}
[-] When 'Test' is invoked 47ms
at <ScriptBlock>, C:\src\git\My-Module\Public\Utility\Get-ParameterValues.ps1: line 74
74: $BoundParameters = Get-Variable -Scope 1 -Name PSBoundParameters -ValueOnly
From C:\src\git\My-Module\Specs\features\Utility\Get- ParameterValues.feature: line 41
Cannot find a variable with the name 'PSBoundParameters'.
DEBUG: Context: {
}
DEBUG: actual:
[+] Then the hashtable 'does not contain' a key for the parameter 61ms
[?] And the resolved parameter value is '<Does Not Exist>'. 30ms
Could not find implementation for step!
At And, C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 43
DEBUG: $Parameter: ValueWithNoDefault
DEBUG: $Value:
[+] Given the value '$null' is provided for the 'ValueWithNoDefault' parameter 14ms
DEBUG: Cmdlet: Test
DEBUG: Context: {
"ValueWithNoDefault": ""
}
DEBUG: $PSBoundParameters = {
"ValueWithNoDefault": ""
}
[-] When 'Test' is invoked 9ms
at <ScriptBlock>, C:\src\git\My-Module\Public\Utility\Get-ParameterValues.ps1: line 74
74: $BoundParameters = Get-Variable -Scope 1 -Name PSBoundParameters -ValueOnly
From C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 41
Cannot find a variable with the name 'PSBoundParameters'.
DEBUG: Context: {
"ValueWithNoDefault": ""
}
DEBUG: actual:
[-] Then the hashtable 'contains' a key for the parameter 20ms
at <ScriptBlock>, C:\src\git\My- Module\Specs\features\steps\Utility\Get-ParameterValues.steps.ps1: line 39
39: ($Context.ContainsKey('Parameter') -and
$actual.ContainsKey($Context.$Parameter)) | Should -Be ($ContainsAKey -eq 'contains')
From C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 42
Expected $true, but got $false.
[?] And the resolved parameter value is '`$null'. 5ms
Could not find implementation for step!
At And, C:\src\git\My-Module\Specs\features\Utility\Get-ParameterValues.feature: line 43
如图所示,仅当$PSBoundParameters
步骤中创建的Test
函数内部为非null / empty /为“ truthy”时,我才“转储” Given
否则我会写一个字符串说它不存在)。在每种情况下,即使将$null
分配给参数,也将定义$PSBoundParameters
并将其存在于Test
函数内部。但是,一旦对Get-ParameterValues
进行了调用,就会在该函数内部尝试从父范围中检索$PSBoundParameters
变量(在这种情况下,Test
函数已经确定该变量存在于该范围内)。但是,我从Pester测试步骤定义中得到了错误消息。不幸的是,这些结果使我更加困惑。