简体   繁体   中英

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:

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?

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.

Is there any Pester expert out there who can help?

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.

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. 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).

The correct code for the Mock is:

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.

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:

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. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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