Мне попалась служебная функция: Get-ParameterValues
, @ Jaykul , а также слегка измененная версия elovelan . Я решил использовать версию elovelan, которая не объявляет параметры. Я могу подтвердить, что, насколько я тестировал в командной строке, функция работает как задумано.
Я включаю функцию в закрытый модуль, который пишу, как общую служебную функцию (конечно, с должным указанием авторства - спасибо @ jaykul и elovelan ! !) и поэтому я пытаюсь написать для него модульные тесты. Однако при выполнении тестов я получаю сообщение об ошибке Cannot find a variable with the name 'PSBoundParameters'.
У меня такое ощущение, что это связано не столько с Пестером, сколько с моим пониманием PowerShell. Я надеюсь, что кто-то здесь, в сообществе, сможет мне помочь.
Сбой теста Пестера с ошибкой Cannot find a variable with the name 'PSBoundParameters'.
Тестируемая функция находится здесь . Вот часть кода теста:
Файл функций
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
, созданной на шаге Given
, только если она не равна нулю / пуста / является «правдивой» (в противном случае я бы написал строку говоря, что не существует). В любом случае, даже если $null
назначено параметру, $PSBoundParameters
определено и существует внутри функции Test
. Однако после вызова Get-ParameterValues
внутри этой функции делается попытка извлечь переменную $PSBoundParameters
из родительской области (в этом случае функция Test
& mdash; которая уже установила, что переменная существует в этой области). Но все же я получаю сообщение об ошибке из определения шага теста Пестера. К сожалению, эти результаты смущают меня еще больше.