2016/05/10

Using Pester to test your Comment Based Help

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 "@lazywinadm" }
            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
                }
            }
        }
    }
}




Here is the code in action:


We can see that I already missed a couple of things on the first functions tested >_< ...


Other posts on Pester




Resources


Some great resources out there helped me to get started:

No comments:

Post a Comment