Generic Pester Tests

Writing good Pester Tests is hard. Use your PowerShell knowledge to make things easier with Generic Pester Tests.

Module Testing

There are many requirements for module manifest to work everywhere. You need a root module to load your .PSM1, you need a version number so PowerShell knows which version to load into the console at runtime, and there should also be identifying information for more information. If you plan on publishing to the PowerShell Gallery you also need to include a description. Why not force those good habits with a Generic module test.

My module structure as show by tree /F /A is as follows for my CIUtils module. You’ll see some familar files. I have my gitignore, my pester tests in the Tests folder, and my module source code in a folder named after the module. Inside of the module folder is the Public and Private folders. Public folders contains functions in PS1 files that are exported to the console when the module is loaded. The private folder is similar but is not exported for outside use.

The first thing my Generic module test does is load the module file. Since my modules are laid out in a predictable fashion I can write a script to handle them.

$ModuleName = Split-Path (Resolve-Path "$PSScriptRoot\..\" ) -Leaf
$ModuleManifest = Resolve-Path "$PSScriptRoot\..\$ModuleName\$ModuleName.psd1"

Import-Module $ModuleManifest

This is something I put at the top of every one of my Pester tests. It loads the module for testing no matter what folder I call it from. Note: Make sure to run ‘Remove-Module $ModuleName’ at the bottom of the test to remove the module from the console.

Now comes the testing of the module itself. I use the Test-ModuleManifest cmdlet to confirm that I have a valid manifest. I also test that the guid is valid, the root module is populated, the version and be converted to the .NET type [Version], and that there is a description for the PowerShell Gallery.

Describe 'Module Information' -Tags 'Command'{
    Context 'Manifest Testing' { 
        It 'Valid Module Manifest' {
            {
                $Script:Manifest = Test-ModuleManifest -Path $ModuleManifest -ErrorAction Stop -WarningAction SilentlyContinue
            } | Should Not Throw
        }
        It 'Valid Manifest Name' {
            $Script:Manifest.Name | Should be $ModuleName
        }
        It 'Generic Version Check' {
            $Script:Manifest.Version -as [Version] | Should Not BeNullOrEmpty
        }
        It 'Valid Manifest Description' {
            $Script:Manifest.Description | Should Not BeNullOrEmpty
        }
        It 'Valid Manifest Root Module' {
            $Script:Manifest.RootModule | Should Be "$ModuleName.psm1"
        }
        It 'Valid Manifest GUID' {
            $Script:Manifest.Guid | SHould be 'df5fcbbb-0f5a-4f6d-a7d1-2127ca0b3955'
        }
        It 'No Format File' {
            $Script:Manifest.ExportedFormatFiles | Should BeNullOrEmpty
        }

        It 'Required Modules' {
            $Script:Manifest.RequiredModules | Should BeNullOrEmpty
        }
    }

    Context 'Exported Functions' {
        It 'Proper Number of Functions Exported' {
            $ExportedCount = Get-Command -Module $ModuleName | Measure-Object | Select-Object -ExpandProperty Count
            $FileCount = Get-ChildItem -Path "$PSScriptRoot\..\$ModuleName\Public" -Filter *.ps1 | Measure-Object | Select-Object -ExpandProperty Count

            $ExportedCount | Should be $FileCount

        }
    }
}

Another thing I do is match all of the PS1 files in the Public folder to make sure my module exports the correct amount of functions. This helps catching renaming issues when a function name and the name of a PS1 file don’t match.

All of these tests should be a good place to start from to make sure your module is up to snuff.

Function Analysis

Generic Pester tests can also be used to check that your functions contain the necessary help for your users. I grab all of the exported functions and check to see if the Synopsis, Description and at least one Example are present. This forces good habits when creating your functions. Make June Blender proud with your help.

Get-Command -Module $ModuleName | ForEach-Object {
    Describe 'Help' -Tags 'Help' {
        Context "Function - $_" { 
            It 'Synopsis' {
                Get-Help $_ | Select-Object -ExpandProperty synopsis | should not benullorempty
            }
            It 'Description' {
                Get-Help $_ | Select-Object -ExpandProperty Description | should not benullorempty
            }
            It 'Examples' {

                $Examples = Get-Help $_ | Select-Object -ExpandProperty Examples | Measure-Object 
                $Examples.Count -gt 0 | Should be $true
            }
        }
    }
}

If you also run Pester tests to keep your modules, functions, or other works in line let me know in the comments. A link to a GitHub Gist is best!