Pester Mock不适用于使用脚本块的Invoke-Command

时间:2016-02-25 12:00:22

标签: unit-testing powershell dependency-injection invoke-command pester

我有一个控制台记录器

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和设置一些全局变量来验证/断言我的测试中动态加载和调用预期函数的工作原理。让Mock在这样的场景中工作会很好,而不是写下那些愚蠢的假货!

任何想法如何起作用或者根本不支持pester?

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

1 个答案:

答案 0 :(得分:3)

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中实现。)

使用呼叫操作员而不是调用命令

以委托方式调用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将正确包含调用操作符可以调用的命令的名称。

我在计算机上测试了这个,现在两个测试都通过了:

enter image description here