Invoke-Command's Mock & Code Coverage issue

218 views
Skip to first unread message

Dexter Dhami

unread,
May 18, 2016, 7:20:23 AM5/18/16
to Pester
Hi Everyone,

I am seeing this issue while mocking the Invoke-Command, the code coverage is missing the Scriptblock passed to the Invoke-Command.

For Example : Below is the function:

Function EnsureRemotePath {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [System.Management.Automation.Runspaces.PSSession]$session,

        [Parameter()]
        [String]$Path
    )
    $pathPresent = Invoke-Command -Session $session -ScriptBlock {Test-Path -Path $using:Path}
    if (-not $pathPresent) {
        Invoke-Command -Session $session -ScriptBlock {$null = New-Item -Path $using:Path -ItemType Directory -Force}
    }
}


Below are the tests, I have written: 

Describe "EnsureRemotePath" -Tags UnitTest {
    $Session = New-PSSession -computername localhost

Context "If the Path exists on the Remote Node" {
# Arrange
Mock -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Equals('Test-Path -Path $using:Path')} -MockWith {$True} -Verifiable
Mock -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Contains('New-Item')}  -MockWith {}

# Act
EnsureRemotePath -Session $session -path 'C:\temp'

# Assert
It 'Should call Invoke-Command to check if the path on RemotelyNode exists' {
Assert-MockCalled -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Equals('Test-Path -Path $using:Path')} -Times 1 -Exactly
Assert-VerifiableMocks
}

It 'Should not create the path as it already exists' {
Assert-MockCalled -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Contains('New-Item')}  -Times 0 -Exactly
}
}

Context "If the Path does NOT exist on the Remote Node, it gets created" {
# Arrange
Mock -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Equals('Test-Path -Path $using:Path')} -MockWith {$false} -Verifiable
Mock -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Contains('New-Item')} -MockWith {} -Verifiable
# Act
EnsureRemotePath -Session $session -path 'C:\temp'

# Assert
It 'Should  create the path.' {
Assert-MockCalled -CommandName Invoke-Command -ParameterFilter { $ScriptBlock.ToString().Contains('New-Item')} -Times 1 -Exactly
Assert-VerifiableMocks
}
}
}

Now when I invoke pester to generate code coverage then the scriptblock used with Invoke-Command are being missed.

PS>Invoke-Pester -CodeCoverage @{ Path = '.\testingpester.ps1'; Function = "EnsureRemotePath" } -TestName "EnsureRemotePath"


Describing EnsureRemotePath
   Context If the Path exists on the Remote Node
    [+] Should call Invoke-Command to check if the path on RemotelyNode exists 3.27
s
    [+] Should not create the path as it already exists 57ms
   Context If the Path does NOT exist on the Remote Node, it gets created
    [+] Should  create the path. 334ms
Tests completed in 3.66s
Passed: 3 Failed: 0 Skipped: 0 Pending: 0 Inconclusive: 0

Code coverage report:
Covered 60.00 % of 5 analyzed commands in 1 file.

Missed commands:

File              Function         Line Command                                   
----              --------         ---- -------                                   
testingpester.ps1 EnsureRemotePath   72 Test-Path -Path $using:Path               
testingpester.ps1 EnsureRemotePath   74 $null = New-Item -Path $using:Path -Ite...


Is there a way in which these missing commands showing up in the code coverage results can be unit tested ?

Regards,
Deepak

Dave Wyatt

unread,
May 18, 2016, 9:24:21 AM5/18/16
to Pester
You could save them as a script block (or function) and test them separately from the calls to Invoke-Command, I suppose.  When you actually pass a script block to Invoke-Command, though, it executes in a separate process that Pester can't track for coverage.

Dexter Dhami

unread,
May 23, 2016, 5:34:39 AM5/23/16
to Pester
Thanks Dave, Let me try that.
Reply all
Reply to author
Forward
0 new messages