Mocking Missing Cmdlet Pipelines with Pester

Following on from the previous the Mocking Missing Cmdlets with Pester and Mocking Missing Cmdlet ErrorAction with Pester posts, I also encountered (yet) another interesting problem when attempting to mock cmdlets that were not present on the test system. This one is pretty similar to the -ErrorAction edge-case but involves the pipeline. Here’s a pseudo test that was failing:

 
Describe 'Mocking Missing Cmdlet Pipeline Example' {
    Function Get-VM { [CmdletBinding()] param ($Name) }
    Function Remove-VM { [CmdletBinding()] param ($Name) }
    
    InModuleScope 'xVMHyper-V' {
 
        It 'Calls Remove-VM' {
            Mock Get-VM -ParameterFilter { $Name -eq 'TestVM' } -MockWith { return 'TestVM' }
            Mock Remove-VM -ParameterFilter { $Name -eq 'TestVM' } -MockWith { }
            Get-VM -Name 'TestVM' | Remove-VM;
            Assert-MockCalled Remove-VM -Scope It;
        }
    }
}

In the above example, we wish to mock both the Get-VM and Remove-VM cmdlets and assert that the Remove-VM cmdlet is called. If we run this on a system that does not have the Hyper-V cmdlets installed we receive the following error:

Executing all tests in 'C:\Users\Iain\Desktop\TestPipeline.Tests.ps1'
Describing Mocking Pipeline Example
Remove-VM : The input object cannot be bound to any parameters for the command either because the command does not
take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At C:\Users\Iain\Desktop\TestPipeline.Tests.ps1:11 char:37
+             Get-VM -Name 'TestVM' | Remove-VM;
+                                     ~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (TestVM:String) [Remove-VM], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Remove-VM

 [-] Calls Remove-VM 1.35s
   Expected Remove-VM to be called at least 1 times but was called 0 times
   at line: 518 in C:\Users\Iain\OneDrive\Powershell\Modules\Pester\Functions\Mock.ps1
Tests completed in 1.35s
Passed: 0 Failed: 1 Skipped: 0 Pending: 0

The error message is fairly self-explanatory. Just like your standard Advanced Function definition, we need to indicate that a parameter needs to be able to accept input via the pipeline using the ValueFromPipeline attribute. To fix this we just need to add the Parameter attribute to our stub function definition:

 
Describe 'Mocking Pipeline Example' {
    Function Get-VM { [CmdletBinding()] param ($Name) }
    Function Remove-VM { [CmdletBinding()] param ( [Parameter(ValueFromPipeline = $true)] $Name) }
    
    InModuleScope 'xVMHyper-V' {
 
        It 'Calls Remove-VM' {
            Mock Get-VM -ParameterFilter { $Name -eq 'TestVM' } -MockWith { return 'TestVM' }
            Mock Remove-VM -ParameterFilter { $Name -eq 'TestVM' } -MockWith { }
            Get-VM -Name 'TestVM' | Remove-VM;
            Assert-MockCalled Remove-VM -Scope It;
        }
    }
}

Whilst it’s not rocket-science – just in case someone else runs into it – I thought it would be worth quickly documenting! Here’s the working example:

Executing all tests in 'C:\Users\Iain\Desktop\TestPipeline.Tests.ps1'
Describing Mocking Pipeline Example
 [+] Calls Remove-VM 74ms
Tests completed in 74ms
Passed: 1 Failed: 0 Skipped: 0 Pending: 0