如何使用Pester模拟对exe文件的调用?

10
在PowerShell中编写脚本时,我需要调用外部可执行文件(.exe)。目前我正在采用TDD方法开发此脚本,因此我需要模拟对此.exe文件的调用。
我尝试了以下代码:
Describe "Create-NewObject" {
    Context "Create-Object" {
        It "Runs" {
            Mock '& "c:\temp\my.exe"' {return {$true}}
            Create-Object| Should Be  $true
        }
    }
}

我收到了这个回复:
Describing Create-NewObject
   Context Create-Object
    [-] Runs 574ms
      CommandNotFoundException: Could not find Command & "C:\temp\my.exe"
      at Validate-Command, C:\Program Files\WindowsPowerShell\Modules\Pester\Functions\Mock.ps1: line 801
      at Mock, C:\Program Files\WindowsPowerShell\Modules\Pester\Functions\Mock.ps1: line 168
      at <ScriptBlock>, C:\T\Create-NewObject.tests.ps1: line 13
Tests completed in 574ms
Passed: 0 Failed: 1 Skipped: 0 Pending: 0 Inconclusive: 0

有没有一种方法可以模拟这种调用,而不需要将它们封装在函数内部?
3个回答

11

我找到了一种模拟对这些可执行文件的调用的方法:

function Create-Object
{
   $exp = '& "C:\temp\my.exe"'
   Invoke-Expression -Command $exp
}

模拟测试应该如下所示:

Describe "Create-NewObject" {
    Context "Create-Object" {
        It "Runs" {
            Mock Invoke-Expression {return {$true}} -ParameterFilter {($Command -eq '& "C:\temp\my.exe"')
            Create-Object| Should Be  $true
        }
    }
}

1
这也是我想到的最好的选择,但我认为“Invoke-Expression”被认为是一种“代码异味”。 - thecoshman

7

很遗憾,从Pester 4.8.1开始:

  • 你无法通过完整路径(例如:C:\Windows\System32\cmd.exe)来mock外部可执行文件。
  • 你可以仅通过文件名(例如:cmd)来mock它们,但要注意,在旧版本的Pester中,mock仅在显式使用.exe扩展名(例如:cmd.exe)调用时才会被调用 - 参见此(已过时)GitHub问题

您自己的解决方法是有效的,但它涉及到Invoke-Expression,这有些棘手;Invoke-Expression通常应该避免使用

这里有一个解决方法,它使用了一个帮助函数Invoke-External,它包装了外部程序的调用,并且作为一个函数本身也可以被mock,使用-ParameterFilter来过滤可执行文件路径:

在你的代码中,定义Invoke-External函数,然后使用它来调用c:\temp\my.exe

# Helper function for invoking an external utility (executable).
# The raison d'être for this function is to allow 
# calls to external executables via their *full paths* to be mocked in Pester.
function Invoke-External {
  param(
    [Parameter(Mandatory=$true)]
    [string] $LiteralPath,
    [Parameter(ValueFromRemainingArguments=$true)]
    $PassThruArgs
  )
  & $LiteralPath $PassThruArgs
}

# Call c:\temp\my.exe via invoke-External
# Note that you may pass arguments to pass the executable as usual (none here):
Invoke-External c:\temp\my.exe

接下来,在你的 Pester 测试中模拟对 c:\temp\my.exe 的调用:

Mock Invoke-External -ParameterFilter { $LiteralPath -eq 'c:\temp\my.exe' } `
  -MockWith { $true }

注意:如果您的代码中只有一个对外部可执行文件的调用,则可以省略-ParameterFilter参数。

1
我尝试了这个,似乎有效。
$PathToExe = 'C:\Windows\System32\wevtutil.exe'
New-Item -Path function: -Name $PathToExe -Value { ... }
Mock $PathToExe { ... }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接