Testing for mandatory parameters with Pester
Asked Answered
K

2

9

I'm trying to figure out how to have Pester test for parameters that are missing:

Find-Waldo.Tests.ps1

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'

Describe 'Mandatory paramters' {
    it  'ComputerName' {
        {
            $Params = @{
                #ComputerName = 'MyPc'
                ScriptName   = 'Test'
            }
            . "$here\$sut" @Params
        } | Should throw
    }
}

Find-Waldo.ps1

Param (
    [Parameter(Mandatory)]
    [String]$ComputerName,
    [String]$ScriptName
)

Function Find-Waldo {
    [CmdletBinding()]
    Param (
        [String]$FilePath
    )

    'Do something'
}

Every time I try to assert the result or simply run the test, it will prompt me for the ComputerName parameter instead of failing the test.

Am I missing something super obvious here? Is there a way to test for the presence of mandatory parameters?

Kurr answered 29/8, 2017 at 9:52 Comment(4)
You shouldn't attempt to test the Mandatory attribute in this way, as per this comment from the teamReisinger
Can you given an example on how to use ((Get-Command Get-Command).Parameters['Name'].Attributes | ? { $_ -is [parameter] }).Mandatory | Should Be $false on a script as in the example above?Kurr
Get-Command works on script files as well: (Get-Command "$here\$sut").ParametersReisinger
Thank you Mathias, I solved it like this (Get-Command "$here\$sut").Parameters['ComputerName'].Attributes.Mandatory | Should be $true If you post this I will mark it as solved.Kurr
P
6

Per the comments from Mathias, you can't really test for whether a Mandatory parameter is missing because PowerShell prompts for it rather than throwing an error. Per the comment he linked to from the Pester team you could use Get-Command to test for the Mandatory parameter setting in the script (assuming it is the only parameter attribute set for that variable)

((Get-Command "$here\$sut").Parameters['ComputerName'].Attributes.Mandatory | Should Be $true

An alternative option would be to not use Mandatory parameters in this instance, and instead have a script block that does a Throw as the default value of the parameter:

Param (
    [String]$ComputerName = $(Throw '-ComputerName is required'),
    [String]$ScriptName
)

If the script is always used as part of an automated process (instead of via user execution) this might be preferred as it allows you to control/capture its behavior and avoids it getting stuck during execution. You can then test the script as you had originally proposed:

Describe 'Mandatory paramters' {
    it  'ComputerName' {
        {
            $Params = @{
                #ComputerName = 'MyPc'
                ScriptName   = 'Test'
            }
            . "$here\$sut" @Params
        } | Should throw '-ComputerName is required'
    }
}
Prepositor answered 29/8, 2017 at 11:4 Comment(0)
D
1

Although the accepted answer indicates that this isn't possible, it actually is possible. Here is the solution that I developed to solve for this problem.

It 'Should fail when no priority is specified, for a valid process name' {
    { 
        $ScriptBlock = {
            Import-Module -Name $args[0]
            Set-ProcessPriority -Name System
        }
        Start-Job -ScriptBlock $ScriptBlock -ArgumentList $HOME/git/ProcessPriority/src/ProcessPriority | Wait-Job | Receive-Job 
    } | Should -Throw
}

What you'll notice from the above example is:

🚀 The code being tested has been wrapped in a PowerShell ScriptBlock

🚀 We invoke a PowerShell background job, containing the test code

🚀 We wait for the background job to complete, and then receive the results

🚀 If you run the Get-Job command, you'll notice that there is a job in the Blocked status

The exception that's thrown by the background job is similar to the following:

The Wait-Job cmdlet cannot finish working, because one or more jobs are blocked waiting for user interaction. Process interactive job output by using the Receive-Job cmdlet, and then try again.

You'll notice that I hard-coded the filesystem path to the module. I am not sure how to pass this as an argument into the "outer" ScriptBlock that Pester is invoking for us. Perhaps someone has a suggestion on how to accomplish that final piece of the puzzle.

What's uniquely interesting about PowerShell background jobs is that you can actually resume a job in the Blocked status, and it will prompt you for input, even though it threw the earlier exception.

Dispenser answered 18/6, 2020 at 8:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.