在 Pester 中参数化 Mock ParameterFilter 时未解析字符串参数
String parameter is not resolved when parameterizing Mock ParameterFilter in Pester
我正在使用 Pester 测试库(5.0.2 版)来测试我的 PowerShell 脚本(5.1 版)并模拟其依赖项。
Pester 有一个 Mock
方法可以用来模拟依赖关系。更多信息 here.
我正在尝试创建一个包装此 Mock 方法的辅助方法,以使我的代码更具可读性:
Function MockVstsInput {
Param(
[Parameter(Mandatory=$true, Position=1)]
[string]$inputName,
[Parameter(Mandatory=$true, Position=2)]
[string]$returnValue
)
Mock Get-VstsInput {return $returnValue} -ParameterFilter { $Name -eq $inputName}
}
在这个辅助方法中,我正在模拟具有参数 $Name
的依赖项 Get-VstsInput
然后我在我的测试脚本中使用这段代码:
MockVstsInput "targetapi" "api-name"
意思是如果 Get-VstsInput
是用 $Name
参数“targetapi”调用的,那么它应该 return “api-name”。通常这种参数化适用于其他语言(f.e.: C# 或 Java),但这里 $inputName
字符串未在 MockVstsInput 方法中解析。
当我在生产代码中调用 Get-VstsInput
方法时:
$newapi=Get-VstsInput -Name targetapi
然后在日志中我有以下 Mock 信息:
Mock: Running mock filter { $Name -eq $inputName } with context: Name = targetapi.
Mock: Mock filter did not pass.
我们可以看到 $inputName
字符串在我的脚本块中没有解析,所以没有发生模拟。
到目前为止我已经尝试过但没有成功:
交换谓词脚本块中相等比较的成员{ $inputName -eq $Name}
在脚本块中使用$ExecutionContext.InvokeCommand.ExpandString($inputName)
解析$inputName
字符串
使用 [ScriptBlock]::create($Name -eq $inputName)
创建一个脚本块,然后在 -ParameterFilter
中使用它
最后但同样重要的是,我尝试调用我的脚本块的 GetNewClosure
,但它也没有帮助:{ $Name -eq $inputName}.GetNewClosure()
您认为我的问题的根本原因是什么?预先感谢所有帮助!
你很接近。这完全取决于您如何构建所需的脚本块。
对于 ParameterFilter 脚本块,您需要使用反引号对 $Name 进行转义,以便将其创建为变量。 $inputName 将替换为我们的变量值,因此您需要用引号引起来。
# $inputName = 'Daniel'
$sb_param = [scriptblock]::Create("`$Name -eq '$inputName'")
这样脚本块中的最终语句看起来像
{ $Name -eq 'Daniel' }
同样,在我们的 MockWith 块中,我们也需要用引号括起来,否则脚本块不会包含字符串,而是无效的命令
# $returnValue = 'Hi Daniel'
$sb_return = [scriptblock]::Create("'$returnValue'")
变成
{ 'Hi Daniel' }
如果我们在创建脚本块时不包含引号,脚本块将包含语句 Hi Daniel
,它将因未找到命令而失败
这是一个工作示例中的所有内容
Describe 'Setting up a Mock from a Function' {
BeforeAll {
Function Get-VstsInput {
[CmdletBinding()]
Param(
$Name
)
'Not Mocked'
}
Function MockVstsInput {
Param(
[Parameter(Mandatory = $true, Position = 1)]
[string]$inputName,
[Parameter(Mandatory = $true, Position = 2)]
[string]$returnValue
)
$sb_param = [scriptblock]::Create("`$Name -eq '$inputName'")
$sb_return = [scriptblock]::Create("'$returnValue'")
Mock Get-VstsInput -MockWith $sb_return -ParameterFilter $sb_param
}
MockVstsInput -inputname 'Daniel' -returnValue 'Hi Daniel'
}
It 'Should mock' {
$test = Get-VstsInput -Name 'Daniel'
$test | Should -Be 'Hi Daniel'
}
}
澄清一下,这是一个范围界定问题。发生的事情是您稍后在不同的范围内将这些脚本块提供给 运行 的模拟命令。当您 运行 模拟命令时,变量 $inputName
和 $returnValue
在调用时未在这些脚本块中定义。这些变量仅在 MockVstsInput
函数中可用,并在函数完成后被清除。
为了说明这一点,下面的代码可以工作,但我不建议这样做,因为如果你 运行 函数不止一次定义不同的模拟,你将覆盖全局变量每次影响任何先前定义的 Mocks
Function MockVstsInput {
Param(
[Parameter(Mandatory = $true, Position = 1)]
[string]$inputName,
[Parameter(Mandatory = $true, Position = 2)]
[string]$returnValue
)
$global:mockInputName = $inputName
$global:mockReturnValue = $returnValue
Mock Get-VstsInput -ParameterFilter {$Name -eq $global:mockInputName} -MockWith {$global:mockReturnValue}
}
因此,与其为 2 个参数脚本块提供稍后将不再在范围内解析的变量,不如使用硬编码的变量值创建脚本块。
此外,请参阅 this answer 了解使用 BeforeEach { }
的方法
我正在使用 Pester 测试库(5.0.2 版)来测试我的 PowerShell 脚本(5.1 版)并模拟其依赖项。
Pester 有一个 Mock
方法可以用来模拟依赖关系。更多信息 here.
我正在尝试创建一个包装此 Mock 方法的辅助方法,以使我的代码更具可读性:
Function MockVstsInput {
Param(
[Parameter(Mandatory=$true, Position=1)]
[string]$inputName,
[Parameter(Mandatory=$true, Position=2)]
[string]$returnValue
)
Mock Get-VstsInput {return $returnValue} -ParameterFilter { $Name -eq $inputName}
}
在这个辅助方法中,我正在模拟具有参数 $Name
Get-VstsInput
然后我在我的测试脚本中使用这段代码:
MockVstsInput "targetapi" "api-name"
意思是如果 Get-VstsInput
是用 $Name
参数“targetapi”调用的,那么它应该 return “api-name”。通常这种参数化适用于其他语言(f.e.: C# 或 Java),但这里 $inputName
字符串未在 MockVstsInput 方法中解析。
当我在生产代码中调用 Get-VstsInput
方法时:
$newapi=Get-VstsInput -Name targetapi
然后在日志中我有以下 Mock 信息:
Mock: Running mock filter { $Name -eq $inputName } with context: Name = targetapi.
Mock: Mock filter did not pass.
我们可以看到 $inputName
字符串在我的脚本块中没有解析,所以没有发生模拟。
到目前为止我已经尝试过但没有成功:
交换谓词脚本块中相等比较的成员
{ $inputName -eq $Name}
在脚本块中使用
$ExecutionContext.InvokeCommand.ExpandString($inputName)
解析$inputName
字符串使用
中使用它[ScriptBlock]::create($Name -eq $inputName)
创建一个脚本块,然后在-ParameterFilter
最后但同样重要的是,我尝试调用我的脚本块的
GetNewClosure
,但它也没有帮助:{ $Name -eq $inputName}.GetNewClosure()
您认为我的问题的根本原因是什么?预先感谢所有帮助!
你很接近。这完全取决于您如何构建所需的脚本块。
对于 ParameterFilter 脚本块,您需要使用反引号对 $Name 进行转义,以便将其创建为变量。 $inputName 将替换为我们的变量值,因此您需要用引号引起来。
# $inputName = 'Daniel'
$sb_param = [scriptblock]::Create("`$Name -eq '$inputName'")
这样脚本块中的最终语句看起来像
{ $Name -eq 'Daniel' }
同样,在我们的 MockWith 块中,我们也需要用引号括起来,否则脚本块不会包含字符串,而是无效的命令
# $returnValue = 'Hi Daniel'
$sb_return = [scriptblock]::Create("'$returnValue'")
变成
{ 'Hi Daniel' }
如果我们在创建脚本块时不包含引号,脚本块将包含语句 Hi Daniel
,它将因未找到命令而失败
这是一个工作示例中的所有内容
Describe 'Setting up a Mock from a Function' {
BeforeAll {
Function Get-VstsInput {
[CmdletBinding()]
Param(
$Name
)
'Not Mocked'
}
Function MockVstsInput {
Param(
[Parameter(Mandatory = $true, Position = 1)]
[string]$inputName,
[Parameter(Mandatory = $true, Position = 2)]
[string]$returnValue
)
$sb_param = [scriptblock]::Create("`$Name -eq '$inputName'")
$sb_return = [scriptblock]::Create("'$returnValue'")
Mock Get-VstsInput -MockWith $sb_return -ParameterFilter $sb_param
}
MockVstsInput -inputname 'Daniel' -returnValue 'Hi Daniel'
}
It 'Should mock' {
$test = Get-VstsInput -Name 'Daniel'
$test | Should -Be 'Hi Daniel'
}
}
澄清一下,这是一个范围界定问题。发生的事情是您稍后在不同的范围内将这些脚本块提供给 运行 的模拟命令。当您 运行 模拟命令时,变量 $inputName
和 $returnValue
在调用时未在这些脚本块中定义。这些变量仅在 MockVstsInput
函数中可用,并在函数完成后被清除。
为了说明这一点,下面的代码可以工作,但我不建议这样做,因为如果你 运行 函数不止一次定义不同的模拟,你将覆盖全局变量每次影响任何先前定义的 Mocks
Function MockVstsInput {
Param(
[Parameter(Mandatory = $true, Position = 1)]
[string]$inputName,
[Parameter(Mandatory = $true, Position = 2)]
[string]$returnValue
)
$global:mockInputName = $inputName
$global:mockReturnValue = $returnValue
Mock Get-VstsInput -ParameterFilter {$Name -eq $global:mockInputName} -MockWith {$global:mockReturnValue}
}
因此,与其为 2 个参数脚本块提供稍后将不再在范围内解析的变量,不如使用硬编码的变量值创建脚本块。
此外,请参阅 this answer 了解使用 BeforeEach { }