简体   繁体   中英

Why Pester does not mock a cmdlet with two parameters?

I'm trying to do some Pester testing, and I get strange error "A positional parameter cannot be found" (for private Python cmdlet), is this a limitation of Pester or something is wrong with my code below?

TestModule.psm1 code:

#public function:
Function Create-Db 
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [string]$database
    )

    Python 'Files\create_db.py' '--DBMS=SQLSERVER -d $database'
}

#private (not exported) function:
Function Python
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory, Position=1)]
        [string]$scriptFile,
        [Parameter(Position=2)]
        [string]$args
    )

    $python ='C:\Python27\python.exe'
    Push-Location $PSScriptRoot

    $python = Start-Process -FilePath $python -ArgumentList @($scriptFile,$args) -Wait -NoNewWindow -PassThru
    if($python.ExitCode -ne 0)
    {
        throw "Python script", $scriptFile, "failed"
    }

    Pop-Location
}

Pester code for the function:

$scriptDirectory = (Split-Path -Parent $MyInvocation.MyCommand.Path) -replace "Test$"
Import-Module $scriptDirectory\TestModule.psm1 -Force

Describe "Create-Db test" {
    Context "Create database" {

        Mock -ModuleName TestModule Python -Verifiable { return; }
        Create-Db -database "test_database"

        It "Python has been called" {
            Assert-VerifiableMocks
        }
    }
}

When I execute the test code I get this error:

Describing Create-Db test
   Context Create database
    [-] Error occurred in Context block 1.35s
      ParameterBindingException: A positional parameter cannot be found that accepts argument '--DBMS SqlServer -d test_database'.
      at Test-ParameterFilter, C:\Program Files\WindowsPowerShell\Modules\Pester\3.3.14\Functions\Mock.ps1: line 1086

$args is an automatic variable which contains all non-bound arguments to non-advanced functions. And it is interpreted by Pester as such. When mocked command called, Pester capture $PSBoundParameters and $args as indication of passed arguments. Later Pester splat captured values to parameter filter routine.

The "bug" in your code is that you use $args as normal parameter to your function and that confuse Pester. When mocked Python called, Pester see:

$PSBoundParameters = @{
    scriptFile = 'Files\create_db.py'
    args = '--DBMS=SQLSERVER -d $database'
}
$args = '--DBMS=SQLSERVER -d $database'

Later Pester call parameter filter script with equivalent of such arguments:

-scriptFile: 'Files\create_db.py' -args: '--DBMS=SQLSERVER -d $database' '--DBMS=SQLSERVER -d $database'

Since parameter filter script does not define any parameter, which can accept positional argument '--DBMS=SQLSERVER -d $database' , you got ParameterBindingException .

You probably can call such behavior a bug in Pester. Since advanced functions does not populate $args automatic variable, it should not be captured in the first place. Pester already have protection to not capture $args from parent scope, it just need addition protection to not capture $args when mocking advanced function or cmdlet.

But you really should not using $args as normal parameter. You better to change parameter name to Arguments and use Args as alias:

[Parameter(Position=2)]
[Alias('Args')]
[string]$Arguments

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM