简体   繁体   English

Mock 函数应该如何接受 Pester 中的管道输入?

[英]How should a Mock function accept pipeline input in Pester?

I am very new to Pester and I am trying to build tests for a very small and simple function in PowerShell:我对 Pester 很陌生,我正在尝试在 PowerShell 中为一个非常小而简单的函数构建测试:

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

}

This function just starts notepad if it is not running, else if it is running it stops it.如果记事本未运行,则此功能仅启动记事本,否则,如果它正在运行,则将其停止。

The test that I have designed is this:我设计的测试是这样的:

$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!"
    }
}

The above tests run as expected.上述测试按预期运行。

How do I rewrite the Stop-Process function so that I can specify that this version is for accepting pipeline input?如何重写Stop-Process函数,以便可以指定此版本用于接受管道输入?

I tried this, but it is not working:我试过这个,但它不起作用:

$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!"
    }
}

Since the Stop-Process function is accepting pipeline input my objective is to Mock the function similar to that, not create a general Stop-Process function which accepts no params.由于Stop-Process函数正在接受管道输入,我的目标是模拟类似的函数,而不是创建一个不接受任何参数的通用Stop-Process函数。

Is there any Pester expert out there who can help?有没有 Pester 专家可以提供帮助?

You typically shouldn't need to manually define parameters within a Mock because the parameters of the function being mocked are created within the Mock automatically.您通常不需要在Mock手动定义参数,因为被Mock的函数的参数是在 Mock 中自动创建的。

The reason your Stop-Process Mock isn't working is because you have defined it with the -ParameterFilter property which results in it only being called when the defined Parameter is true.您的Stop-Process Mock 不工作的原因是因为您已经使用-ParameterFilter属性定义了它,这导致它仅在定义的 Parameter 为真时才被调用。 Your filter is not working because you are referencing the object type as a string ( "System.Diagnostics.Process" ) rather than as a type (which are declared with square brackets).您的过滤器不起作用,因为您将对象类型引用为字符串( "System.Diagnostics.Process" )而不是类型(用方括号声明)。

The correct code for the Mock is: Mock 的正确代码是:

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

I asked this question of the Pester development team directly and they suggested a simpler solution of just -ParameterFilter { $InputObject } as it will be null if it's not populated with a valid object of that type.直接向 Pester 开发团队询问了这个问题,他们提出了一个更简单的解决方案,即-ParameterFilter { $InputObject }因为如果它没有填充该类型的有效对象,它将为 null。

Here's a complete example:这是一个完整的例子:

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
    }

}

Also FYI, since Pester 3.4.4 there is now a New-MockObject command that allows you to create fake objects of any type:另外仅供参考,从 Pester 3.4.4 开始,现在有一个New-MockObject命令,允许您创建任何类型的假对象:

New-MockObject is a Pester function (introduced in Pester 3.4.4) that allows you to create "fake" objects of almost any type to run in Pester mocks. New-MockObject 是一个 Pester 函数(在 Pester 3.4.4 中引入),它允许您创建几乎任何类型的“假”对象以在 Pester 模拟中运行。 These "fake objects" allow your mocks to return the same type as the function it was mocking to pass the result to entities that are strongly typed.这些“假对象”允许您的模拟返回与其模拟的函数相同的类型,以将结果传递给强类型的实体。

It's not necessary in your scenario but I thought you might find it relevant/interesting.在您的场景中没有必要,但我认为您可能会发现它相关/有趣。

You can mock a function accepting pipeline values like any other function:您可以像任何其他函数一样模拟接受管道值的函数:

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!"
    }
}

Please note also that your mocked Stop-Process has typos in it so it won't work.另请注意,您Stop-Process存在拼写错误,因此无法正常工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM