Considering the below Powershell code, is there a way to mock $host.ui.PromptForChoice
without the internalMenuWrapper
function?
<#
.Synopsis
wrap the menu so we can mock calls to it
#>
function internalMenuWrapper {
param (
[Parameter(Mandatory=$true)]
$prompt,
[Parameter(Mandatory=$true)]
$options
)
return = $host.ui.PromptForChoice("Waiting for user input", $prompt, [System.Management.Automation.Host.ChoiceDescription[]]$options, 0)
}
<#
.Synopsis
Create a menu with an array of choices and return the result
#>
function Invoke-Menu($prompt, $opts) {
$options = @()
foreach ($opt in $opts) {
$options += $(new-object System.Management.Automation.Host.ChoiceDescription $opt)
}
$index = internalMenuWrapper $prompt $options
$opts[$index]
}
Describe 'Invoke-Menu' {
Context "when called" {
It "returns the object that was selected" {
#mock fails
Mock internalMenuWrapper { return 0 }
$result = Invoke-Menu "test menu" @("pass", "fail")
$result | Should -Be "pass"
}
}
}
As Mike Shepard points out in a comment, mocking methods isn't supported in Pester, only commands can be mocked (cmdlets, functions, aliases, external programs).
You can work around the issue by using the Get-Host
cmdlet instead of $host
and mock that:
function Invoke-Menu($prompt, $choices) {
$choiceObjects = [System.Management.Automation.Host.ChoiceDescription[]] $choices
# Use Get-Host instead of $host here; $host cannot be mocked, but Get-Host can.
$index = (Get-Host).ui.PromptForChoice("Waiting for user input", $prompt, $choiceObjects, 0)
$choices[$index]
}
Describe 'Invoke-Menu' {
Context "when called" {
It "returns the object that was selected" {
# Mock Get-Host with a dummy .ui.PromptForChoice() method that instantly
# returns 0 (the first choice).
Mock Get-Host {
[pscustomobject] @{
ui = Add-Member -PassThru -Name PromptForChoice -InputObject ([pscustomobject] @{}) -Type ScriptMethod -Value { return 0 }
}
}
Invoke-Menu 'test menu' '&pass', '&fail' | Should -Be '&pass'
}
}
}
As you point out on GitHub, if the suggested Read-Choice
cmdlet is ever implemented (as a PS-friendly wrapper around $host.ui.PromptForChoice()
), it could be mocked directly (and there would be no custom code to test).