Getting Started With Pester


Today I wanted to take a step back and cover something I should have written about ages ago: getting started with Pester from the very beginning.

I’ve written about using Pester for infrastructure testing before, but I realized I never covered the absolute basics. So if you’re completely new to testing your PowerShell code, this one’s for you!

Think of Pester as your PowerShell function’s quality control inspector. Just like you’d test a new car before driving it off the lot, Pester lets you test your PowerShell functions to make sure they work exactly as expected.

Pester is PowerShell’s built-in testing framework that helps you:

  • Verify your functions do what you think they do
  • Catch bugs before they cause problems
  • Make sure changes don’t break existing functionality
  • Document how your functions are supposed to work

Good news! If you’re running PowerShell 5.1 or later, Pester is already installed. However, you might want to update to the latest version:

1
2
3
4
5
# Check your current version
Get-Module -Name Pester -ListAvailable

# Install the latest version
Install-Module -Name Pester -Force -Scope CurrentUser

Let’s create a realistic but simple function that we can write tests for. We’ll make a function that formats person names consistently:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Format-PersonName {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$FirstName,
        
        [Parameter(Mandatory)]
        [string]$LastName,
        
        [switch]$LastNameFirst
    )
    
    # Clean up the input - remove extra spaces and fix capitalization
    $CleanFirst = (Get-Culture).TextInfo.ToTitleCase($FirstName.Trim().ToLower())
    $CleanLast = (Get-Culture).TextInfo.ToTitleCase($LastName.Trim().ToLower())
    
    if ($LastNameFirst) {
        return "$CleanLast, $CleanFirst"
    } else {
        return "$CleanFirst $CleanLast"
    }
}

This function takes a first and last name, cleans them up (proper capitalization, removes extra spaces), and formats them either as “First Last” or “Last, First” depending on the switch parameter.

Save this function in a file called Format-PersonName.ps1 (matching the function name is a good practice).

Now let’s create our first Pester test! In the same directory where you saved your Format-PersonName.ps1 file, create a new file called Format-PersonName.Tests.ps1. Notice the .Tests.ps1 naming convention - this is important because Pester automatically looks for files with this pattern when running tests.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
BeforeAll {
    # This neat trick imports the function file automatically
    . $PSCommandPath.Replace('.Tests.ps1','.ps1')
}

Describe "Format-PersonName" {
    It "Should format names in standard order by default" {
        $result = Format-PersonName -FirstName "john" -LastName "doe"
        $result | Should -Be "John Doe"
    }
    
    It "Should format names with last name first when switch is used" {
        $result = Format-PersonName -FirstName "jane" -LastName "smith" -LastNameFirst
        $result | Should -Be "Smith, Jane"
    }
    
    It "Should handle extra spaces correctly" {
        $result = Format-PersonName -FirstName "  bob  " -LastName "  johnson  "
        $result | Should -Be "Bob Johnson"
    }
    
    It "Should fix capitalization" {
        $result = Format-PersonName -FirstName "ALICE" -LastName "brown"
        $result | Should -Be "Alice Brown"
    }
}

Let’s break down what’s happening here:

  • BeforeAll - This runs once before all the tests in this file. We use it to import our function so it’s available for testing. The $PSCommandPath.Replace('.Tests.ps1','.ps1') trick automatically finds the matching function file by replacing the test file extension with .ps1.
  • Describe - This groups related tests together. Think of it as a test category or test suite. You typically have one Describe block per function you’re testing.
  • It - This is an individual test case. Each It block should test one specific behavior or scenario. The text after It should describe what the test is checking.
  • Should - This is where we make assertions - checking if our function did what we expected. Pester has many different assertion types like -Be, -BeGreaterThan, -Contain, etc.

The beauty of Pester is how readable it is. You can literally read the test out loud: “Format-PersonName should format names in standard order by default.” This makes your tests self-documenting!

Now for the moment of truth! Open PowerShell in the directory where your files are and run:

1
Invoke-Pester -Path ".\Format-PersonName.Tests.ps1"

If everything works correctly, you should see something like:

1
2
3
4
5
6
7
Starting discovery in 1 files.
Discovery found 4 tests in 52ms.
Running tests.

[+] Format-PersonName.Tests.ps1 234ms (64ms|170ms)
Tests completed in 294ms
Tests Passed: 4, Failed: 0, Skipped: 0 NotRun: 0

That green [+] means all your tests passed! 🎉

Want to see more details about what’s happening? Try running with detailed output:

1
Invoke-Pester -Path ".\Format-PersonName.Tests.ps1" -Output Detailed

This will show you each individual test as it runs, which is really handy when you have lots of tests or when troubleshooting.

Let’s see what happens when something goes wrong. Here’s a quick way to make a test fail - just change one of your Should -Be assertions to Should -Not -Be:

1
2
3
4
It "Should format names in standard order by default" {
    $result = Format-PersonName -FirstName "john" -LastName "doe"
    $result | Should -Not -Be "John Doe"  # Changed -Be to -Not -Be
}

Run the test again and you’ll see:

1
2
[-] Format-PersonName.Should format names in standard order by default 23ms (22ms|1ms)
 Expected 'John Doe' to not be 'John Doe', but it was 'John Doe'.

Pester clearly tells you what went wrong and shows both the expected and actual values. This makes debugging much easier! Don’t forget to change it back to Should -Be when you’re done experimenting.

As you think of edge cases or find bugs, you can easily add more tests:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Describe "Format-PersonName" {
    # ... existing tests ...
    
    It "Should handle mixed case names" {
        $result = Format-PersonName -FirstName "mCdOnAlD" -LastName "o'cOnNoR"
        $result | Should -Be "Mcdonald O'connor"
    }
    
    It "Should handle hyphenated names" {
        $result = Format-PersonName -FirstName "mary-jane" -LastName "watson-parker"
        $result | Should -Be "Mary-Jane Watson-Parker"
    }
}

You might be thinking “this seems like a lot of work for a simple function.” But here’s the thing - once you have these tests, you can:

  1. Refactor confidently - Want to improve your function? Run the tests afterward to make sure you didn’t break anything.
  2. Document behavior - Your tests show exactly how the function should work.
  3. Catch regressions - If you accidentally break something later, the tests will tell you immediately.
  4. Build trust - Other people (including future you) can trust that the function works as documented.

This is just the beginning! Pester can do much more:

  • Test error conditions and parameter validation
  • Mock external dependencies
  • Test entire modules
  • Generate code coverage reports
  • Run tests automatically in CI/CD pipelines

But for now, try creating a simple function of your own and writing a few basic tests for it. You’ll be surprised how quickly you start catching bugs and thinking more clearly about how your functions should behave.

At the end of the month I will join the ‘owner/maintainer’ of Pester, Jakub Jares for a whole workshop on Pester called ‘Teach yourself Pester’ at this year’s PowerShell Conference Europe, so join us in Malmö to see what it’s all about!
I’ll also most likely continue on this path of making more blog posts on the subject, to try and help people get more comfortable with the subject.

Testing doesn’t have to be complicated. Start small, test the basics, and build from there. Your future self (and your colleagues) will thank you when your PowerShell functions just work, every time.

Happy testing! 😊