Migrating from Pester 4 to Pester 5

SAPIEN’s PowerShell Studio and PrimalScript products both include integrated Pester testing. In this article, we will cover some changes in the Pester 4 to Pester 5 upgrade.

The most significant change in the release of Pester 5 is that it now runs in two phases: Discovery and Run. In the Discovery phase, all of your test files are examined, discovering all of the Pester blocks (Describes, Contexts, Its, etc.). The Run phase executes the tests and evaluates the results.

Discovery and Run Phases

All code should now be put into It, BeforeAll, BeforeEach, AfterAll, or AfterEach. Put no code directly into Describe, Context or on the top of your file without wrapping it in one of these blocks (unless for good reason).

If code is not put in the proper place, it will run in Discovery, and the results will not be available during Run. During the Run phase, the internal tree of containers, blocks, and tests are run.

Variables from Discovery are not available in Run unless you attach them to a Describe, Context, or It block using Foreach or TestCases.

Source: https://pester.dev/docs/usage/data-driven-tests#migrating-from-pester-v4

Dot sourcing

Dot sourcing imports the test code file to allow the tests to use it.

Note: PowerShell Studio does not require you to dot source the source file within the *.Tests.ps1 script. It will automatically dot source the original file for you when you use the Run Pester Test command.

Previously in Pester 4, if you chose to run the pester file by clicking the Run button instead of Run Pester Test when you were to dot source a file, you would put it globally without being in a pester block.

In the jump to Pester 5, if you dot source this file, you would receive an error as such:

CommandNotFoundException: The term 'Add-Numbers' is not recognized as the name of a cmdlet, function, script file, or operable program.

To make the function available to the test, we must put it in a BeforeAll block.

#Pester 5
# at the top of Add-Numbers.Tests.ps1
BeforeAll { 
    . $PSScriptRoot/Add-Numbers.ps1


#Pester 5
BeforeAll {
    # Add-Numbers.Tests.ps1 - .Tests.ps1 + .ps1 = Add-Numbers.ps1
    . $PSCommandPath.Replace('.Tests.ps1','.ps1')

Note: $MyInvocation.MyCommand.Path should NOT be used in a BeforeAll block. Running $MyInvocation.MyCommand.Path in any function will make the Path property undefined.


Mockings will now default in the block in which they are placed. Below are two scenarios that will work correctly.

Debugging with Mocking

Pester 5 now has the ability for mocks to be debugged.

Tag Parameter

The tag parameter is now available on Describe, Context, and It to filter on any level. This is useful for choosing which tests you want to run.

For example, if you only want to run the parts of code with the tag “Acceptance” and not include the code within the “WindowsOnly” tag, then you would run this command:

Invoke-Pester $path -Tag "Acceptance" -ExcludeTag "WindowsOnly"

Tags with wildcards

Tags are now compared as -like wildcards. For example, if you don’t want to spell out the whole tag or want to include tags that contain that word in it, then you can use a wildcard:

Invoke-Pester $path -ExcludeT "Accept*", "*nuxonly" | Out-Null


Invoke-Pester was modified from Pester 4 to a more simple interface, excluding hashtables.

The advanced configuration just takes the Pester configuration object and nothing else:

Pesterv4: foreach

Using this code in Pester 5, the foreach is evaluated during Discovery because the whole script is run. However, BeforeAll won’t run until the Run phase, making the $files variable not defined during Discovery, and the tests are not generated.

BeforeAll { 
    # check all script files
    $files = Get-ChildItem *.ps1

foreach ($file in $files) { 
    Describe "$file is correct" { 
        It "has empty line at end" { 
            # ...

        It "has UTF-8 encoding" {
            # ...

This can be solved by defining the code that is used to generate tests into the BeforeDiscovery block (which will be introduced in Pester 5.1 below).

Pester Test Results

In Pester 4, each test result displayed in the console shows how many tests were ‘Passed’, ‘Failed’, ‘Skipped’, ‘Pending’, and ‘Inconclusive’.

Pester 4 output

In the Pester 5.0 update, there is no longer a ‘Pending’ or ‘Inconclusive’ state. Tests that would have resulted in these states are now marked as ‘Skipped‘ test states. This also applies when using Set-ItResult.

New ‘Result’ Object

There is a new Pester ‘Result’ object. Most of the information in the object’s tree is unprocessed to allow you to work with raw data that you can use in your code.


Pester 5 added the function ConvertTo-Pester4Result, which converts a Pester5 object to a Pester 4 compatible-object. This is a temporary way to solve issues that may arrive when migrating to Pester 5, keeping old code compatible.

$pester5Result = Invoke-Pester -Passthru
$pester4Result = $pester5Result | ConvertTo-Pester4Result

-Script Option

The -Script option was renamed to -Path and takes paths only; it does not take hashtables. Parameterized scripts are not implemented in Pester 5.0, which is solved in 5.1.

Pester 5.1 additions

In Pester 5.1, there were also fixes from 5.0 to 5.1. Here are some of the basic improvements.

‘BeforeDiscovery’ Block

Since Pester 5 code is run in different phases, a BeforeDiscovery block was added in 5.1, which groups code together if you need it to run during discovery.

BeforeDiscovery { 
    $testConfigs = Get-ChildItem -Path $PSScriptRoot/TestConfigs

Describe "Platform dependent tests" {
    foreach ($config in $testConfigs) {
        It "Runs for config $($config.Name)" { 
            # ...

-ForEach & -Testcases with hashtables

In Pester 5.1, TestsCases is no longer limited to only arrays; you can now take an array of hashtables.

To define examples for a test, -ForEach is used on an -It block. Inside of the @( ) array, a hashtable is provided @{}.

-ForEach defines the $_ variable even when used with an array of hashtables. In that case, $_ will be set to the current hashtable.


<> Templates

Test and block names can contain values surrounded by <> in their name. Those will be considered a template and will be expanded. Any variable available in scope can be expanded using this syntax, not just the data from -ForEach.


Dot Notation

You can use <_> to expand the $_. You can also use dot-notation <user.name> to navigate into the object.


New-Fixture has been added back in Pester 5. It will now, by default, fail to show when a template was not completed.

Related Links






Please continue providing your feedback. Many of the new features included in the service builds are suggestions from users like you. Submit your suggestions or feature requests on the Wish List and Feature Requests forum or the Feature Requests page.