3

我对 Pester 很陌生,我正在尝试在 PowerShell 中为一个非常小而简单的函数构建测试:

function Toggle-Notepad {
    if (-not ( Get-Process notepad -ErrorAction SilentlyContinue ) )
    {
        Start-Process -FilePath Notepad
    }
    else
    {
        get-process notepad | stop-process
    }

}

如果记事本没有运行,这个函数只会启动它,否则如果它正在运行,它就会停止它。

我设计的测试是这样的:

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

Describe "Toggle-Notepad" {
    Mock Stop-Process { "Process object passed! Stopping notepad!" }
    Mock Start-Process { "Notepad is not running,starting it!" } -ParameterFilter { $Filepath -eq "Notepad" }

    It "Starts notepad if it is not running" {
        Toggle-Notepad | Should be "Notepad is not running,starting it!"
    }

    It "Stops notepad if it is running" {
        Toggle-Notepad | Should be "Process object passed ! Stopping notepad!"
    }
}

上述测试按预期运行。

如何重写Stop-Process函数以便我可以指定此版本用于接受管道输入?

我试过这个,但它不工作:

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

Describe "Toggle-Notepad" {
    Mock stop-process { "Process object passed ! Stopping notepad" } -ParameterFilter { $InputObject -is "System.Diagnostics.Process" }
    Mock Start-Process {"Notepad is not running,starting it!"} -ParameterFilter { $Filepath -eq "Notepad" }

    It "Starts notepad if it is not running" {
        Toggle-Notepad | Should be "Notepad is not running,starting it!"
    }

    It "Stops notepad if it is running" {
        Toggle-Notepad | Should be "Process object passed ! Stopping notepad!"
    }
}

由于该Stop-Process函数接受管道输入,我的目标是模拟与此类似的函数,而不是创建不Stop-Process接受任何参数的通用函数。

有没有 Pester 专家可以提供帮助?

4

2 回答 2

2

您通常不需要在 a 中手动定义参数,Mock因为被模拟的函数的参数是在 Mock 中自动创建的。

您的Stop-ProcessMock 不起作用的原因是因为您已经使用-ParameterFilter属性定义了它,导致它仅在定义的 Parameter 为 true 时被调用。您的过滤器不起作用,因为您将对象类型引用为字符串 ( "System.Diagnostics.Process") 而不是类型(用方括号声明)。

Mock 的正确代码是:

Mock stop-process { "Process object passed ! Stopping notepad!" } -ParameterFilter { $InputObject -is [System.Diagnostics.Process[]] }

直接问了 Pester 开发团队的这个问题,他们提出了一个更简单的解决方案,-ParameterFilter { $InputObject }如果它没有填充该类型的有效对象,它将为 null。

这是一个完整的例子:

Describe 'Mocking stop-process' {

    Mock Stop-Process { 
        Write-Host "Mock stopping: $InputObject"
    } -ParameterFilter { $InputObject }

    It 'Should mock stopping the notepad process' {
        Get-Process notepad | Stop-Process | Should Be $null
    }

}

另外仅供参考,从 Pester 3.4.4 开始,现在有一个New-MockObject命令允许您创建任何类型的假对象:

New-MockObject 是一个 Pester 函数(在 Pester 3.4.4 中引入),它允许您创建几乎任何类型的“假”对象以在 Pester 模拟中运行。这些“假对象”允许您的模拟返回与其模拟的函数相同的类型,以将结果传递给强类型的实体。

在您的场景中没有必要,但我认为您可能会发现它相关/有趣。

于 2017-04-24T15:25:35.593 回答
0

您可以像任何其他函数一样模拟接受管道值的函数:

Describe "Toggle-Notepad" {
    Mock Stop-Process {
        [CmdletBinding()]
        param(
            [Parameter(ValueFromPipeline = $true)]
            $InputObject
        )
        return "Process object passed! Stopping notepad"
    } -ParameterFilter { $InputObject -is "System.Diagnostics.Process" }

    Mock Start-Process {
        return "Notepad is not running, starting it!"
    } -ParameterFilter { $Filepath -eq "Notepad" }

    It "Starts notepad if it is not running" {
        Toggle-Notepad | Should be "Notepad is not running, starting it!"
    }

    It "Stops notepad if it is running" {
        Toggle-Notepad | Should be "Process object passed! Stopping notepad!"
    }
}

另请注意,您的嘲笑Stop-Process中有错别字,因此它不起作用。

于 2017-03-25T23:33:11.590 回答