我有一个控制台记录器

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在这种情况下工作,而不是写那些愚蠢的假货,将是一件好事!

有什么想法会如何工作,还是完全没有pester支持?

PS:如果按顺序复制,所有代码均有效

最佳答案

Pester的模拟功能拦截范围

Pester仅拦截特定范围内对模拟函数的调用。我认为控制此范围的唯一受支持方法是使用 InModuleScope 。这使您可以指定Pester应该拦截使用InModuleScope指定的模块中对模拟函数的调用。

在Pester拦截的范围内未调用Common-Write-Log-Console
在“测试2”中,对Common-Write-Log-Console的“调用”发生在此调用内的某个位置:

Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType

您尚未指定Pester应该在实现了Invoke-Command的任何模块内拦截对模拟函数的调用。 (我怀疑您能否实现此目标,因为Invoke-Command随WMF一起提供,可能未在PowerShell中实现。)

使用 call 运算符而不是调用命令

当作为代表调用PowerShell命令时,我建议使用 & call operator而不是Invoke-Command。如果您重写此行
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType


& $logger -logText $logText

测试2,应根据需要调用Common-Write-Log-Console的模拟程序。

PowerShell委托(delegate)的句柄只是包含函数名称的字符串

调用PowerShell委托(delegate)时,您只需要一个包含函数名称的字符串即可。如果您重写此行
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})


$loggers.Add("Console_Logger", 'Common-Write-Log-Console')
$logger将正确包含调用操作员可以调用的命令的名称。

我在计算机上对此进行了测试,现在两项测试均通过:

unit-testing - Pester Mock对使用脚本块的Invoke-Command不起作用-LMLPHP

关于unit-testing - Pester Mock对使用脚本块的Invoke-Command不起作用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35626695/

10-09 16:56