Pester: Cannot mock external commands

Created on 26 Aug 2015  路  18Comments  路  Source: pester/Pester

I am trying to test code that calls on external binaries (in my example certreq and openssl.exe). Obviously I want to mock these calls in my tests but when I try, Pester complains:

You did not declare a mock of the certreq Command.

Here's a small reproducable example:

Mock echo { }
echo foo
Assert-MockCalled echo

Running Powershell 3.0 on Windows 7.

Mocking Question

Most helpful comment

Regarding the parameterfiltering, I did like this to mock a call to an external program.

function Invoke-Nuget
{
    & "/tools/nuget.exe" $args
}

Invoke-Nuget list foo.bar -prerelease


In my test i do like this:
Assert-MockCalled -CommandName Invoke-Nuget -Times 1 -ParameterFilter {$args[0] -eq "list" -and $args[1] -eq "foo.bar" -and $args[2] -eq "-prerelease"}
This works fine.

All 18 comments

The echo is an alias of Write-Output so you need to assert-mockcalled Write-Output not echo to make it work.

But you still may mock echo and call echo in the sut.

Well echo was just an example. But in my case I want to mock certreq and ../vendor/openssl.exe. I get the same error: Assert-MockCalled complains that I never mocked the command.

Mocking external commands is really flaky, unfortunately. My personal preference is to wrap all calls to those commands in PowerShell functions, and then I can easily mock calls to those functions instead. For example:

function Invoke-CertReq
{
    certreq.exe @args
}

Well, I was afraid of that. Thanks anyway. :)

Ok, follow up to your suggestion: how do I use this to verify parameters sent to the Invoke-CertReq function?

Mock Invoke-CertReq -ParameterFilter { ??? }

I'd probably do some analysis of the default $args variable inside the ParameterFilter script block. You'd have this same problem trying to mock the external command directly, though. Anything that's not a PowerShell command, has no parameter metadata to make it easier on you.

Regarding the parameterfiltering, I did like this to mock a call to an external program.

function Invoke-Nuget
{
    & "/tools/nuget.exe" $args
}

Invoke-Nuget list foo.bar -prerelease


In my test i do like this:
Assert-MockCalled -CommandName Invoke-Nuget -Times 1 -ParameterFilter {$args[0] -eq "list" -and $args[1] -eq "foo.bar" -and $args[2] -eq "-prerelease"}
This works fine.

@kajbonfils Are you showing a solution or asking a question? :)

@nohwnd Sorry, that wasn't clear. It was a solution. I have updated the response.

I assume that the issue can be closed now. Feel free to reopen if you need more assistance.

Can we get some clarification on the final answer? Is wrapping the native executable and testing against the wrapper the recommended method at this point?

Yes, wrapping native commands to functions and mocking functions is recommended way.

@nohwnd et al, is there additional guidance on this for Pester 5 and mocking az / databricks / kubectl / jq cli tools and other python, go or exe/com external cli tools?

Examples of being able to pester test python / go cli tools such as these would be useful, esp within powershellcore.

Creating a wrapper for mocking each cli tool's callout seems a bit inefficient and a workaround to accommodate the testing framework.

Nice! This seems to work, since the cli tools are nuget/pip installed packages.

Mock 'jq.exe' -MockWith { $true }
Mock 'databricks.exe' -MockWith { $true }

exe extension is optional?

@asears Yes, it is possible since forever, but the version without .exe works since https://github.com/pester/Pester/issues/1043

Hi!

If I want to mock an external command with a parameter, e.g. this

qwinsta /server localhost

Is this possible without using a wrapper?
(I've managed to make it work for a parameterless call to qwinsta, but once I added the arguments the mock stopped working).
(I'm using Pester 5)

how does your mock look like?

Thank you for your reply/question. However, I think the problem I experienced with external mocks was related to the issue I reported earlier today: https://github.com/pester/Pester/issues/1696. The problem, which I wasn't able to diagnose at that time, seems to be related to a non-empty scriptblock (as described in #1696).

However, the failure output is a little different, so I prepared a minimal example to trigger it, in case it would prove helpful.

BeforeAll {
    function FunctionThatInvokesQwinsta ($Server='localhost') { 
        qwinsta /server $Server
    }
}

Describe "Mocking external command bug?" {

    It 'works for mock with empty scriptblock' {
        Mock qwinsta { }
        FunctionThatInvokesQwinsta | Should -Invoke qwinsta
    }

    It 'reports CommandNotFoundException when non-empty scriptblock' {
        Mock qwinsta { "Some output" }
        FunctionThatInvokesQwinsta | Should -Invoke qwinsta
    }
}

The output is

[-] Mocking external command bug?.reports CommandNotFoundException when non-empty scriptblock 104ms (103ms|1ms)
 CommandNotFoundException: Could not find Command Some output

Tests completed in 258ms
Tests Passed: 1, Failed: 1, Skipped: 0 NotRun: 0

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Mahendra1260 picture Mahendra1260  路  50Comments

nohwnd picture nohwnd  路  22Comments

nohwnd picture nohwnd  路  129Comments

nohwnd picture nohwnd  路  23Comments

iRon7 picture iRon7  路  20Comments