unit-testing - Pester Mock 不适用于使用脚本 block 的 Invoke-Command

标签 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

}

为动态调用添加Logger

 $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. 总是失败。我通过创建一个伪造的记录器函数来解决这个问题,而不是使用模拟并设置一些全局变量来在我的测试中验证/断言预期函数的动态加载和调用有效。让模拟在这种情况下工作会很好,而不是编写那些愚蠢的假货!

知道它是如何工作的吗?或者说它根本不受 pester 支持?

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

最佳答案

Pester 的模拟函数拦截范围

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

Common-Write-Log-Console 未在 Pester 拦截的范围内调用

在“测试 2.”中,对 Common-Write-Log-Console 的“调用”发生在此调用中的某处:

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

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

使用调用操作符代替调用命令

当作为委托(delegate)调用 PowerShell 命令时,我建议使用 & call operator而不是 Invoke-Command。如果你重写这一行

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

作为

& $logger -logText $logText

测试 2,应该根据需要调用 Common-Write-Log-Console 的 mock。

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

调用 PowerShell 委托(delegate)时,您只需要一个包含函数名称的字符串。如果你重写这一行

$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})    

作为

$loggers.Add("Console_Logger", 'Common-Write-Log-Console')

$logger 将正确包含调用运算符(operator)可以调用的命令的名称。


我在我的电脑上测试了这个,现在两个测试都通过了:

enter image description here

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

相关文章:

c# - 从控制台运行参数化测试

java - 在Java中注入(inject)类字段

powershell - Powershell 中的多个管道

powershell - 使用 pester 测试 powershell DSC 脚本类 - 找不到类型 [ClassName]

flutter - 如何在 Flutter 中使用 SharedPreferences 和 Injectable?

java - 将通用服务与其实现和子类型绑定(bind)

java - Java EE 7 属性文件配置的最佳实践建议是什么?

iPhone:应用程序测试和核心定位

c# - 对 SqlCommand 的扩展方法进行单元测试的方法

version-control - 让 IIS 网站进入版本控制