In some cases, it is desirable to mock commands that are not available on the test system. One possible approach in these circumstances is to create a function that reflects the command first, then mock the function.
For example, consider a function that creates and configures a DNS zone with a predefined set of parameter values:
function New-DnsZone {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[String]$Name
)
$params = @{
Name = $Name
DynamicUpdate = 'Secure'
ReplicationScope = 'Domain'
}
if (-not (Get-DnsServerZone $Name -ErrorAction SilentlyContinue)) {
Add-DnsServerPrimaryZone @params
}
}
It may not be desirable to install the DNS module on a development system when testing the script. To mock and verify that Add-DnsServerPrimaryZone is called, a function must be created first:
Describe CreateDnsZone {
BeforeAll {
function Get-DnsServerZone { }
function Add-DnsServerPrimaryZone { }
Mock Get-DnsServerZone
Mock Add-DnsServerPrimaryZone
}
It 'When the zone does not exist, calls Add-DnsServerPrimaryZone' {
New-DnsZone -Name name
Assert-MockCalled Add-DnsServerPrimaryZone
}
}
Creating the function first is enough to satisfy the tests, but the approach is basic. The test will not fail if the parameter names that are used are incorrect.
A more advanced function to mock may be created by visiting a system with the command installed and retrieving the following param block:
$command = Get-Command Add-DnsServerPrimaryZone
[System.Management.Automation.ProxyCommand]::GetParamBlock($command)
The first of the parameters from the block is shown as follows:
[Parameter(ParameterSetName='ADForwardLookupZone', ValueFromPipelineByPropertyName=$true)]
[Parameter(ParameterSetName='ADReverseLookupZone', ValueFromPipelineByPropertyName=$true)]
[Parameter(ParameterSetName='FileForwardLookupZone', ValueFromPipelineByPropertyName=$true)]
[Parameter(ParameterSetName='FileReverseLookupZone', ValueFromPipelineByPropertyName=$true)]
[ValidateNotNull()]
[ValidateNotNullOrEmpty()]
[string]
${ResponsiblePerson},
Adding a reasonable parameter block will improve the overall quality of the tests. The tests will fail if a non-existent parameter is used, or if an invalid parameter combination is used.
I refer to the functions used like this as stub commands, and have written a module that will interrogate other modules and generate a psm1 file, which can be imported by a stub module. This approach is based on, but is more detailed than, the method described previously.
The module is available in the PowerShell Gallery and can be installed as follows: Install-Module Indented.StubCommand -Scope CurrentUser.