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

时间:2016-06-20 15:55:23

标签: unit-testing powershell mocking pester

在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

有没有办法模拟这种调用而不将它们封装在一个函数中?

2 个答案:

答案 0 :(得分:5)

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

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 :(得分:1)

很遗憾,自Pester 4.8.1起:

  • 无法通过其完整路径(例如C:\Windows\System32\cmd.exe)模拟外部可执行文件
  • 可以仅通过文件名(例如,cmd)对它们进行模拟,但是请注意,在较早的Pester版本中,模拟仅被调用用于明确使用.exe扩展名(例如cmd.exe)-请参见this (obsolete) GitHub issue

Your own workaround是有效的,但是它涉及Invoke-Expression,这很尴尬; Invoke-Expression should generally be avoided

这是一种使用辅助函数Invoke-External的变通办法,该函数可以自动包装外部程序的调用,并且可以作为一个模拟功能来模拟外部程序的调用,使用-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参数。