Pester Testing And PowerShell Development Workflow

In this post I’m going to have a look at Pester testing and PowerShell development workflow. I’ll be using Visual Studio and Team Foundation Server/Visual Studio Team Services to keep it simple. What I’m going to show won’t be news for developers but this can help to put testing in perspective for non developers.

Developer WorkFlow
Firstly lets have a look at the typical points during development when you want to run your tests and then we will dive into the details of getting it to work.

  • Running tests locally while developing.
    This depends on the development methodology you follow.

    • If you practice TDD you will be writing unit tests as you develop, a few lines at a time.
    • Depending on the size of the script most other developers will be writing a function and then a unit test for it or the whole script and then multiple unit tests. Remember it is better to write the tests while the code is still fresh in your mind.These scenarios work seamlessly if you use Visual Studio with PowerShell tools, Visual Studio will discover your Pester tests and show them in the test explorer.

Side Note: You can also use the tests to make development easier by mocking out infrastructure that is tricky to test like deleting user accounts in AD or deleting VMs.

  • Running tests with a gated checkin. In this case you want VSTS/TFS to run the build and test suite before the code is committed to the main code repository. VSTS/TFS  supports check in policies and one of them requires the build to pass before it is committed.
  • Running the tests after checkin as part of the build process. This is how most developers will run it and although VSTS/TFS doesn’t have native support for Pester tests you can run the tests using a Powershell script. Usually the whole test suite is executed at this point and can include integration tests.

For the last two scenarios to work you have to be able to install the Pester and PSCodeAnalyzer modules as well as any other modules required for your script on the build server. If you are using VSTS hosted build agents this is a bit of a problem since you need administrator access to install modules in the default locations for PowerShell. You can however manually import a module from a local folder during the build process. You can include the modules in you project or store them in a central location or repo and share them between your scripts. Some modules can even be installed from the Marketplace as a build step. If you are hosting your own Visual Studio Agents or using TFS this is not  a problem, you can install them as part of your build server or on demand.

Running A Pester Test
To execute Pester tests you have run a PowerShell script that will call Invoke-Pester. By default Pester will look for all scripts called *.Test.ps1 recursively but you can specify the script to test and even the specific functions in a script. Read the full documentation here.

Retrieving Test Results
Pester can output the test results in NUnit XML consult the documentation for the syntax and output options. VSTS/TFS can display this format on the test results tab. It doesn’t seem to do the same for the CodeCoverage results but you can still attach the output of the code coverage to your build artifacts by using VSTS/TFS commands.

Alternatively you can roll your own test results by passing the -PassThru parameter to Invoke-Pester, this will return the results as a typed object. You can use this to transform the results in any format you want.

Francois Delport

Unit Testing PowerShell With Pester

In this post I’m going to cover unit testing PowerShell with Pester.

As your PowerShell scripts become more complicated it can be difficult and time consuming to manually test them. This is especially true when your script relies on external resources like ActiveDirectory or Azure infrastructure. Pester is a framework specifically designed to unit test PowerShell scripts. It enables mocking, assertions and running tests using nunit or Visual Studio test explorer among others.

How to install it
In PowerShell 5.0 and greater you can install the Pester module by running:

Install-Module Pester

If for some reason you are unable to do that you can manually download it from Github and copy it to a folder that is in your $ENV:PSModulePath environment variable or use choclatey.

Invoke-Expression ((new-object   net.webclient).DownloadString('https://chocolatey.org/install.ps1'))
choco install pester -y

To confirm that the module is loaded run:

Get-Module -ListAvailable

Authoring Tests
I highly recommend using Visual Studio with PowerShell tools installed for your PowerShell projects, this demo will be using it. In VS create a PowerShell Script Project, add a PowerShell test to it using the PowerShell Test template.

Unit Testing PowerShell With Pester

The VS test explorer will pick up your PowerShell tests, same it does for other unit tests. Pester has a naming convention to follow for the tests and scripts, the test file should have the same name as the script to test but with .test before the extensions. If you don’t follow the convention the tests will show as greyed out in the VS test explorer.

Unit Testing PowerShell With Pester

The structure of a test
At the top of each test file you have to dot source the script file that will be tested.

.\PathTo\ScriptFiletoTest.ps1

In the demo project this is done using logic based on the naming convention. The structure of the actual test looks like this:

Unit Testing PowerShell With Pester

The Description section will group your tests together and this is also the name you will see in VS test explorer. Context creates a scope for variables and mocked objects. It will contain the code for your test and at least one assertion or else the test will not run.

Mocking And Assertions
You can mock calls to your own functions as well as calls to PowerShell modules. For example if you want Get-Date to return a specific date in your test you can do this:

Mock Get-Date { New-Object DateTime (2000, 1, 1) }

$date = GetMyOwnDate
It "It Should Be The Year 2000"{
    ($date).year | Should be "2000"
}

If you want to verify that a method is called with specific parameters you can do this:

Mock Myfunction {} -Verifiable -ParameterFilter {$myparam -eq $folder}
...
Assert-VerifiableMocks

I have a sample project on GitHub to show some of the functionality of Pester but read the wiki on their GitHub repo to get the full picture .

Some Errors I Encountered And Tips

  • The nuget package didn’t work for since nuget doesn’t understand PowerShell projects.
  • If your tests doesn’t run and you get a yellow exclamation mark next to the test in text explorer one cause could be that there are no assertions in your test “It” block.
  • I also tried mocking some ActiveDirectory calls on a Windows 1o machine. To get the PowerShell modules you have to install Remote Server Administration tools for your client OS.
  • If you want to mock the calls inside another module instead of just mocking your calls to those functions follow this guide.

Francois Delport