4 minute read

Update 2019/07/26: It seems that the AST to Help content parameter comparison doesn’t work in PowerShell Core. See the example suggested by @mgeorgebrown89 (Thank you!)
Update 2016/11/25: Updated the code to support functions where there is no parameter declared (Thanks Wojciech Sciesinski !)

I remember attending a meeting on Pester presented by Dave Wyatt back in November 2014 during my first MVP Summit.

A couple of well known PowerShellers were there: Boe Prox, Emin Atac, Adam Driscoll, Mike Robbins, Fabien Dibot, Jan Egil Ring, Steve Murawski, … It was a great event, great to finally meet all those guys…

Anyway, at the time Pester looked pretty neat but since I only played with it a couple of times and never really invest or commit myself to create tests for each of my scripts or modules.

During the Microsoft MVP Summit 2014 week (Bellevue, WA, USA) Evening event organized by Dave Wyatt on Pester.

What is Pester ?

Pester is a Behavior-driven development (BDD) based test runner for PowerShell. Pester provides a framework for running Unit Tests to execute and validate PowerShell commands. Pester follows a file naming convention for naming tests to be discovered by pester at test time and a simple set of functions that expose a Testing DSL for isolating, running, evaluating and reporting the results of PowerShell commands.

Pester tests can execute any command or script that is accessible to a pester test file. This can include functions, Cmdlets, Modules and scripts. Pester can be run in ad hoc style in a console or it can be integrated into the Build scripts of a Continuous Integration system.

Pester also contains a powerful set of Mocking Functions that allow tests to mimic and mock the functionality of any command inside of a piece of PowerShell code being tested. See Mocking with Pester.

My first pester

So I decided that from now on, I’ll do my best to deliver tests with any new scripts or modules. At first, It won’t be perfect for sure but I have to start somewhere….

Goal

My first goal is to verify the Comment Based Help for each of the functions included in the module ADSIPs. I want to make sure that I have a description, parameter description and examples.

I easily see this pester test being expanded to test each of the examples defined in my functions.

Pester Tests

  • Synopsis is not Null or Empty
  • Description is not Null or Empty
  • Parameters descriptions is not Null or Empty
  • Parameters count declared in Help is equal to the Parameter found in the Abstract Syntax Trees (AST)
  • Examples count are greater than 0
  • Notes contains my information
  • Author
  • Website
  • Twitter Handle
  • Github Account
Describe "AdsiPS Module" -Tags "Module" {

    # Import Module
    #import-module C:\Test\AdsiPS\AdsiPS.psd1

    #$FunctionsList = (get-command -Module ADSIPS).Name
    $FunctionsList = (get-command -Module ADSIPS | Where-Object -FilterScript { $_.CommandType -eq 'Function' }).Name

    FOREACH ($Function in $FunctionsList)
    {
        # Retrieve the Help of the function
        $Help = Get-Help -Name $Function -Full

        $Notes = ($Help.alertSet.alert.text -split '\n')

        # Parse the function using AST
        $AST = [System.Management.Automation.Language.Parser]::ParseInput((Get-Content function:$Function), [ref]$null, [ref]$null)

        Context "$Function - Help"{

            It "Synopsis"{ $help.Synopsis | Should not BeNullOrEmpty }
            It "Description"{ $help.Description | Should not BeNullOrEmpty }
            It "Notes - Author" { $Notes[0].trim() | Should Be "Francois-Xavier Cat" }
            It "Notes - Site" { $Notes[1].trim() | Should Be "Lazywinadmin.com" }
            It "Notes - Twitter" { $Notes[2].trim() | Should Be "@lazywinadmin" }
            It "Notes - Github" { $Notes[3].trim() | Should Be "github.com/lazywinadmin" }

            # Get the parameters declared in the Comment Based Help
            $RiskMitigationParameters = 'Whatif', 'Confirm'
            $HelpParameters = $help.parameters.parameter | Where-Object name -NotIn $RiskMitigationParameters

            # Get the parameters declared in the AST PARAM() Block
            $ASTParameters = $ast.ParamBlock.Parameters.Name.variablepath.userpath

            $FunctionsList = (get-command -Module $ModuleName | Where-Object -FilterScript { $_.CommandType -eq 'Function' }).Name



            It "Parameter - Compare Count Help/AST" {
                $HelpParameters.name.count -eq $ASTParameters.count | Should Be $true
            }

            # Parameter Description
            If (-not [String]::IsNullOrEmpty($ASTParameters)) # IF ASTParameters are found
            {
                $HelpParameters | ForEach-Object {
                    It "Parameter $($_.Name) - Should contains description"{
                        $_.description | Should not BeNullOrEmpty
                    }
                }

            }

            # Examples
            it "Example - Count should be greater than 0"{
                $Help.examples.example.code.count | Should BeGreaterthan 0
            }

            # Examples - Remarks (small description that comes with the example)
            foreach ($Example in $Help.examples.example)
            {
                it "Example - Remarks on $($Example.Title)"{
                    $Example.remarks | Should not BeNullOrEmpty
                }
            }
        }
    }
}

Note that in PowerShell Core, It seems that the AST to Help content parameter comparison doesn’t work. Thanks to @mgeorgebrown89 who suggested the following:

foreach ($param in $ASTParameters) {
    It "parameter `"$param`" has a .PARAMTER description" {
         (Get-Help $functionName -Parameter $param).description | Should -Not -BeNullOrEmpty
    }
}

Here is the code in action:

We can see that I already missed a couple of things on the first functions tested … :-/

Other Pester posts

Resources

Some great resources out there helped me to get started:

Leave a comment