Pester Mock 不适用于使用脚本块的 Invoke-Command
Pester Mock does not work for Invoke-Command using script block
我有一个控制台记录器
function Common-Write-Log-Console
{
param (
[Parameter(Mandatory=$true)]
[string] $logText
)
$textToOutput = [String]::Format("{0}:{1}", [System.DateTime]::Now.ToString(), $logText)
Write-Output ($textToOutput)
}
然后我有包装函数,它通过动态加载来调用它
function Common-Write-Log-WithInvoke
{
param (
[Parameter(Mandatory=$true)]
[string] $logText
)
foreach($logger in $loggers.Values)
{
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
}
}
另一个直接调用它的包装函数
function Common-Write-Log-WithoutInvoke
{
param (
[Parameter(Mandatory=$true)]
[string] $logText,
[string] $verbosityLevel = "Normal",
[string] $logType = "Info"
)
Common-Write-Log-Console $logText
}
为动态调用添加记录器
$loggers = @{}
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})
现在我有几个 Pester 测试
# pester tests
Describe "Common-Write-Log" {
It "Test 1. Calls all log sources when log sources are called directly - **this test passes**" {
# Arrange
$expectedLogText = "test message"
Mock Common-Write-Log-Console -Verifiable -ParameterFilter { $logText -eq $expectedLogText}
# Act
Common-Write-Log-WithoutInvoke "test message"
# Assert
Assert-VerifiableMocks
}
It "Test 2. Calls all log sources when log sources are called through Invoke-Command - **this test fails**" {
# Arrange
$expectedLogText = "test message"
Mock Common-Write-Log-Console -Verifiable -ParameterFilter { $logText -eq $expectedLogText}
# Act
Common-Write-Log-WithInvoke "test message"
# Assert
Assert-VerifiableMocks # This statement fails as actual function "Common-Write-Log-Console" is called instead of the mocked one
}
}
测试 2. 总是失败。我通过创建一个伪造的记录器函数来解决问题,而不是在我的测试中使用 mock 并将一些全局变量设置为 verify/assert ,动态加载和调用预期函数有效。让模拟在这种情况下工作会很好,而不是编写那些愚蠢的假货!
知道它是如何工作的吗?或者它根本不受 pester 支持?
PS:如果按顺序复制,所有代码都有效
Pester 的模拟函数拦截范围
Pester 仅拦截对特定范围内模拟函数的调用。我认为唯一受支持的控制此范围的方法是使用 InModuleScope
。这允许您指定 Pester 应该拦截对您使用 InModuleScope
.
指定的模块中的模拟函数的调用
Common-Write-Log-Console
未在 Pester 拦截的范围内调用
在 "Test 2." 中,"call" 到 Common-Write-Log-Console
发生在这个调用中的某处:
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
您没有指定 Pester 应该拦截对实现的任何模块 Invoke-Command
内的模拟函数的调用。 (我怀疑您能否实现此目的,因为 Invoke-Command
随 WMF 一起提供,可能未在 PowerShell 中实现。)
使用调用运算符而不是调用命令
当作为委托调用 PowerShell 命令时,我建议使用 &
call operator 而不是 Invoke-Command
。如果你重写这一行
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
作为
& $logger -logText $logText
测试 2,应该根据需要为 Common-Write-Log-Console
调用模拟。
PowerShell 委托的句柄只是一个包含函数名称的字符串
调用 PowerShell 委托时,您只需要一个包含函数名称的字符串。如果你重写这一行
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})
作为
$loggers.Add("Console_Logger", 'Common-Write-Log-Console')
$logger
将正确包含调用操作员可以调用的命令的名称。
我在我的电脑上测试了这个,现在两个测试都通过了:
我有一个控制台记录器
function Common-Write-Log-Console
{
param (
[Parameter(Mandatory=$true)]
[string] $logText
)
$textToOutput = [String]::Format("{0}:{1}", [System.DateTime]::Now.ToString(), $logText)
Write-Output ($textToOutput)
}
然后我有包装函数,它通过动态加载来调用它
function Common-Write-Log-WithInvoke
{
param (
[Parameter(Mandatory=$true)]
[string] $logText
)
foreach($logger in $loggers.Values)
{
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
}
}
另一个直接调用它的包装函数
function Common-Write-Log-WithoutInvoke
{
param (
[Parameter(Mandatory=$true)]
[string] $logText,
[string] $verbosityLevel = "Normal",
[string] $logType = "Info"
)
Common-Write-Log-Console $logText
}
为动态调用添加记录器
$loggers = @{}
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})
现在我有几个 Pester 测试
# pester tests
Describe "Common-Write-Log" {
It "Test 1. Calls all log sources when log sources are called directly - **this test passes**" {
# Arrange
$expectedLogText = "test message"
Mock Common-Write-Log-Console -Verifiable -ParameterFilter { $logText -eq $expectedLogText}
# Act
Common-Write-Log-WithoutInvoke "test message"
# Assert
Assert-VerifiableMocks
}
It "Test 2. Calls all log sources when log sources are called through Invoke-Command - **this test fails**" {
# Arrange
$expectedLogText = "test message"
Mock Common-Write-Log-Console -Verifiable -ParameterFilter { $logText -eq $expectedLogText}
# Act
Common-Write-Log-WithInvoke "test message"
# Assert
Assert-VerifiableMocks # This statement fails as actual function "Common-Write-Log-Console" is called instead of the mocked one
}
}
测试 2. 总是失败。我通过创建一个伪造的记录器函数来解决问题,而不是在我的测试中使用 mock 并将一些全局变量设置为 verify/assert ,动态加载和调用预期函数有效。让模拟在这种情况下工作会很好,而不是编写那些愚蠢的假货!
知道它是如何工作的吗?或者它根本不受 pester 支持?
PS:如果按顺序复制,所有代码都有效
Pester 的模拟函数拦截范围
Pester 仅拦截对特定范围内模拟函数的调用。我认为唯一受支持的控制此范围的方法是使用 InModuleScope
。这允许您指定 Pester 应该拦截对您使用 InModuleScope
.
Common-Write-Log-Console
未在 Pester 拦截的范围内调用
在 "Test 2." 中,"call" 到 Common-Write-Log-Console
发生在这个调用中的某处:
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
您没有指定 Pester 应该拦截对实现的任何模块 Invoke-Command
内的模拟函数的调用。 (我怀疑您能否实现此目的,因为 Invoke-Command
随 WMF 一起提供,可能未在 PowerShell 中实现。)
使用调用运算符而不是调用命令
当作为委托调用 PowerShell 命令时,我建议使用 &
call operator 而不是 Invoke-Command
。如果你重写这一行
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
作为
& $logger -logText $logText
测试 2,应该根据需要为 Common-Write-Log-Console
调用模拟。
PowerShell 委托的句柄只是一个包含函数名称的字符串
调用 PowerShell 委托时,您只需要一个包含函数名称的字符串。如果你重写这一行
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})
作为
$loggers.Add("Console_Logger", 'Common-Write-Log-Console')
$logger
将正确包含调用操作员可以调用的命令的名称。
我在我的电脑上测试了这个,现在两个测试都通过了: