I’ve been using Pester now for around 4 months now and I can’t imagine developing any significant PowerShell code without it. It’s firmly in my tool belt for developing robust modules and has forced me to consider the way my modules are structured. At this point though I wouldn’t say I follow a strict TDD approach but the more PowerShell development I do the more I think there would be value to this. At the very least I try to ensure that for every cmdlet I develop I at least attempt to put in place a corresponding Pester test to exercise it.
Pester describes itself as the “…ubiquitous test and mock framework for PowerShell” and so far I can testify that my experience using it has been largely pain-free. So how do we go about testing a block of PowerShell code? Here’s a simple function we want to test:
function Add-ClientNameToDatabase {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$clientName,
[Parameter(Mandatory = $true)]
[hashtable]$settings
)
process {
Write-Information "Adding Client Name [$clientName] into database"
$sql = "INSERT into dbo.Clients VALUES($clientName);"
Invoke-Sqlcmd -ServerInstance $settings.TargetServer -Database $settings.TargetDatabase -Query $sql
}
}
And here is an example of the contents of a very simple test (named Add-ClientNameToDatabase.tests.ps1
which exists in a folder name Tests
)
$currentLocation = Get-Location
$folderPath = Join-Path -Path $currentLocation -ChildPath '\Private'
. "$folderPath\Add-CLientNameToDatabase.ps1";
Describe "Add-ClientNameToDatabase" {
Mock Invoke-Sqlcmd {
return @{
result=1
}
}
It 'calls Invoke-SQL at least once in the It block' {
$settings = @{ }
$settings["TargetDatabase"] = "_TargetDatabase"
$settings["TargetServer"] = "_TargetServer"
Add-ClientNameToDatabase -clientName "TestClient" -settings $settings
Assert-MockCalled Invoke-Sqlcmd -Exactly 1 -Scope It
}
}
Set-Location $currentLocation
So there’s a few things going on, in the first part of the code we are setting up the path to the cmdlet we are looking to test and then “dot-sourcing” it into the current scope of our test script. This is also very importing for the mocking to work correctly as the Pester framework will need any functions present in scope to be able to mock them.
We then create a Describe block giving it a name of Add-ClientNameToDatabase
, for the testing that I’ve done I’ve stuck with naming the describe blocks the same as my target test script.
I then create a Mock the Invoke-SqlCmd
function, saying that anytime this function is called within the current scope return a value of 1.
A It block is then added which will then both invoke and assert a test. In this case I’ve also arranged some of the test by composing an array of settings (TargetDatabase
and TargetServer
). The call to the function Add-ClientNameToDatabase
then takes place with the necessary parameters. We then assert that the mock of Invoke-Sqlcmd
has been called exactly once (within the cope of the It block).
Finally a call just to set the current location back to where it started is performed, this is a standard housekeeping call made just in case the calling function has changed the current location.
To run the test, we use the Invoke-Pester
command, in my experience you need to be very conscious of where in the folder structure your current PowerShell session is currently sitting. The way that I’ve dot-sourced the private function in the test means that it will attempt to load the function from a child folder named “Private” from the current location. So to run this test successfully you need to be in a folder above “Private” (so probably in the root folder of your project).
Calling Invoke-Pester
will then execute all tests that end with .tests
in their file name, to execute a specific test the full path to the test (including the name) can be passed as a parameter to Invoke-Pester
. A short summary of the test results is then displayed indicating the number of tests passed and which ones failed, similar to the one below:

In posts that follow I’m hoping to cover some of the more interesting challenges I’ve had with mocking functions, in particular functions that returns values from databases (result sets) and functions that return more complex structures (like from the Az module)