Learn how to build a PowerShell function to better control your Pester tests!

When I first started learning Pester, I struggled. If you're new to Pester and want to learn more, I highly encourage you to check out the Pester Book. It is over 200 pages of everything you ever wanted to know about Pester and testing with PowerShell.

Other than the fact that I was brand new to writing tests with Pester I couldn't quite wrap my head around an easy way to build test scripts and kick them off. I knew I had to use the Invoke-Pester function but I wasn't quite sure how to pass parameters to it, limit test execution and so on.

This is why I built a little helper function for Invoke-Pester and started to build my tests a certain way.

I primarily build Pester integration tests. This means that I use Pester to test the end result of my code; not the actual code itself.

For example, if I have a function called New-AzureVM, a typical Pester test would include:

  • testing to ensure the VM was created with the right name
  • the right operating system was installed
  • it was placed in the right storage account

These are not unit tests that test the function's structure but the overall end result.

I work on a project with dozens of PowerShell modules. I want to write integration tests for each function in that module.

In order to keep things straight, when a new module is built, a new test script is automatically built as well. So, for example, if I have a new module called NewModule.psm1, I will always create a script called NewModule.Tests.ps1 in the same folder. By keeping this naming convention, I can rely on my test script to always be called this and write code inside of the script to depend on this.

At the top of every test script I create are these lines:

#region import modules
$ThisModule = "$($MyInvocation.MyCommand -replace '\.Tests\.ps1$')"
$RequiredModules = $ThisModule,'Module123','Module345' ## This varies if I have any functions inside the test in other modules
Import-Module -Name $RequiredModules -Force
#endregion

These are lines that dynamically find out the module this test script is for as well as any other modules that are required to perform the testing. It then forcefully reloads them.

I then have your basic Describe/Context/It blocks which I won't go into in this post. For more information on these blocks, check out the Pester Book. However, I do use tags. Tags are great to categorize your describe blocks and using the helper function that I'll explain in a minute, allows you to disable tests or limit test runs only to certain describe blocks.

A typical describe block might look like this:

describe -Tag 'Disabled','Integration' New-AzureVM {
    it 'does something' {

    }
}

I use tags to enable/disable certain tests as I've developing them so I don't have to go through every test. I also tend to separate them out from unit vs. integration testing.

Unit tests are fast since you don't actually have to touch the infrastructure. By separating them out, I can just run each test category if I want using my Start-PesterTest helper function.

Finally, I kick off tests with a function called Start-PesterTest. Start-PesterTest is basically a helper function for Invoke-Pester that's easier for me to use for my integration testing. Instructions to use it are in the comment-based help.

My method is still a work in progress. Let me know what you think and I'm always open to suggestions on how to improve!

Join the Jar Tippers on Patreon

It takes a lot of time to write detailed blog posts like this one. In a single-income family, this blog is one way I depend on to keep the lights on. I'd be eternally grateful if you could become a Patreon patron today!

Become a Patron!