How to mock a call to an exe file with Pester?
Asked Answered
I

3

10

Developing a script in PowerShell, I require to call an external executable file(.exe). currently I am developing this script with a TDD approach, therefore I require to mock the called to this .exe file.

I try this :

Describe "Create-NewObject" {
    Context "Create-Object" {
        It "Runs" {
            Mock '& "c:\temp\my.exe"' {return {$true}}
            Create-Object| Should Be  $true
        }
    }
}

I got this response:

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

Is there a way to mock this kind of calls without encapsulate them inside a function?

Inter answered 20/6, 2016 at 15:55 Comment(0)
I
11

I found a way to mock the call to this executable files:

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

And the test with the mock should looks like:

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
        }
    }
}
Inter answered 20/6, 2016 at 16:22 Comment(1)
This is the best option that I've come up with as well, but I believe Invoke-Experssion is considered a 'code smell'.Suffice
D
7

Yes, unfortunately, as of Pester 4.8.1:

  • you cannot mock external executables by their full paths (e.g, C:\Windows\System32\cmd.exe)
  • you can mock them by file name only (e.g., cmd), but beware that in older Pester versions the mock is only called for invocations that explicitly use the .exe extension (e.g., cmd.exe) - see this (obsolete) GitHub issue

Your own workaround is effective, but it involves Invoke-Expression, which is awkward; Invoke-Expression should generally be avoided

Here's a workaround that uses a helper function, Invoke-External, which wraps the invocation of external programs and, as a function, can itself be mocked, using a -ParameterFilter to filter by executable path:

In your code, define the Invoke-External function and then use it to make your call to 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

Then, to mock the call to c:\temp\my.exe in your Pester tests:

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

Note: If you only have one call to an external executable in your code, you can omit the
-ParameterFilter argument.

Divebomb answered 29/6, 2019 at 21:57 Comment(2)
I think, in your Invoke-External function you should use parameter splatting and replace & $LiteralPath $PassThruArgs with & $LiteralPath @PassThruArgs to correctly handle native command argumentsFootslog
@GrzegorzWolszczak, while you may choose to use @ for conceptual clarity, it isn't necessary: when calling external programs, passing an array as an argument implicitly results in splatting, i.e the array's elements are passed as individual arguments.Divebomb
M
1

I tried something this and seemed to work.

$PathToExe = 'C:\Windows\System32\wevtutil.exe'
New-Item -Path function: -Name $PathToExe -Value { ... }
Mock $PathToExe { ... }
Masteratarms answered 5/5, 2020 at 7:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.