我如何用 Pester 模拟 $host.ui.PromptForChoice

How do I mock $host.ui.PromptForChoice with Pester

考虑到下面的 Powershell 代码,有没有办法在没有 internalMenuWrapper 函数的情况下模拟 $host.ui.PromptForChoice

<#
    .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"
        }
    }
}

正如 Mike Shepard 在评论中指出的那样,Pester 不支持 mocking methods,只有 commands 可以模拟(cmdlet、函数、别名、外部程序).

您可以 通过使用 Get-Host cmdlet 而不是 $host 和 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'
      }
  }
}

正如您指出的那样 on GitHub,如果建议的 Read-Choice cmdlet 曾经被实现(作为 $host.ui.PromptForChoice() 的 PS 友好包装器),它可能会被嘲笑直接(并且没有要测试的自定义代码)。