Reputation: 1625
I have a console logger
function Common-Write-Log-Console
{
param (
[Parameter(Mandatory=$true)]
[string] $logText
)
$textToOutput = [String]::Format("{0}:{1}", [System.DateTime]::Now.ToString(), $logText)
Write-Output ($textToOutput)
}
Then I have wrapper function which calls it by dynamically loading it
function Common-Write-Log-WithInvoke
{
param (
[Parameter(Mandatory=$true)]
[string] $logText
)
foreach($logger in $loggers.Values)
{
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
}
}
Another wrapper function which calls it directly
function Common-Write-Log-WithoutInvoke
{
param (
[Parameter(Mandatory=$true)]
[string] $logText,
[string] $verbosityLevel = "Normal",
[string] $logType = "Info"
)
Common-Write-Log-Console $logText
}
Add Loggers for dynamic calling
$loggers = @{}
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})
Now I have couple of Pester tests
# pester tests
Describe "Common-Write-Log" {
It "Test 1. Calls all log sources when log sources are called directly - **this test passes**" {
# Arrange
$expectedLogText = "test message"
Mock Common-Write-Log-Console -Verifiable -ParameterFilter { $logText -eq $expectedLogText}
# Act
Common-Write-Log-WithoutInvoke "test message"
# Assert
Assert-VerifiableMocks
}
It "Test 2. Calls all log sources when log sources are called through Invoke-Command - **this test fails**" {
# Arrange
$expectedLogText = "test message"
Mock Common-Write-Log-Console -Verifiable -ParameterFilter { $logText -eq $expectedLogText}
# Act
Common-Write-Log-WithInvoke "test message"
# Assert
Assert-VerifiableMocks # This statement fails as actual function "Common-Write-Log-Console" is called instead of the mocked one
}
}
Test 2. always fails. I have worked around by creating a fake logger function, instead of using mock and setting some global variables to verify/assert in my test that dynamic loading and calling of intended function works. It would be nice to get the Mock working in such scenario , rather then writing those dumb fakes!
Any ideas how would it work or is it not supported by pester at all?
PS: All code works if copied in order
Upvotes: 3
Views: 2540
Reputation: 4263
Pester only intercepts calls to mocked functions in particular scopes. I think the only supported method of controlling this scope is using InModuleScope
. That allows you to designate that Pester should intercept calls to mocked functions in the module that you have specified using InModuleScope
.
Common-Write-Log-Console
is Not Called in a Scope where Pester InterceptsIn "Test 2.", the "call" to Common-Write-Log-Console
takes place somewhere inside this call:
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
You have not specified that Pester should intercept calls to mocked functions inside whatever module Invoke-Command
is implemented. (I doubt that you could achieve this, because Invoke-Command
is shipped with WMF and probably not implemented in PowerShell.)
When invoking PowerShell commands as delegates I recommend using the &
call operator instead of Invoke-Command
. If you rewrite this line
Invoke-Command $logger -ArgumentList $logText,$verbosityLevel,$logType
as
& $logger -logText $logText
Test 2, should call the mock for Common-Write-Log-Console
as you desire.
When invoking a PowerShell delegate, all you need is a string containing the name of the function. If you rewrite this line
$loggers.Add("Console_Logger", ${function:Common-Write-Log-Console})
as
$loggers.Add("Console_Logger", 'Common-Write-Log-Console')
$logger
will correctly contain the name of the command which the call operator can invoke.
I tested this on my computer and both tests now pass:
Upvotes: 4