Reputation: 237
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
Upvotes: 1
Views: 1244
Reputation: 22132
$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
Upvotes: 4