Pester Gherkin测试:“找不到名称为'PSBoundParameters'的变量”

时间:2018-10-31 02:14:52

标签: powershell pester

我遇到了一个实用函数:@JaykulGet-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测试步骤定义中得到了错误消息。不幸的是,这些结果使我更加困惑。

0 个答案:

没有答案