Last week, you learned about Getting Started with Pester Testing in PowerShell, which covered how to install Pester, write your first test script, and run that script against a module. This week, you’ll dive into more detail with the PowerShell Pester Should command and its operators, which is the basis for writing Pester assertions or tests. These are the heart of Pester and make up the tests for our code.

This post is using Pester version 5.0.2 and PowerShell version 7.0.1 running on Windows 10.

Performing Assertions with Should

The Pester Should command performs tests or assertions in a script. It is used for comparing objects and throwing failures when the test is expected to fail. You use Should inside of It blocks in the test script. Should also has different operators for performing tests.

Last week, you wrote some basic assertions testing your module code in different scenarios: when you didn’t use a parameter and when you did. You used the -Be and -BeOfType operators for performing the tests. Let’s review these assertions:

It "should return 'I got something!'" {
    Get-Something | Should -Be 'I got something!'
}
It "should be a string" {
    Get-Something | Should -BeOfType System.String
}
$thing = 'a dog'
It "should return 'I got' follow by parameter value" {
    Get-Something -ThingToGet $thing | Should -Be "I got $thing!"
}
It "should be a string" {
    Get-Something -ThingToGet $thing | Should -BeOfType System.String
}

Running Get-Something without a parameter should return the default string “I got something!” while specifying a parameter should return “I got” followed by the string. In both cases, the output should be of System.String type.

In addition to saying what something should be, you can also test for the negative and say what things should not be. You can perform this using the -Not operator. Keep an eye out for this operator in the upcoming examples.

Let’s jump into our other Should operators.

BeExactly, BeLike, BeLikeExactly

This will do a case-sensitive comparison of two objects. This is like the Be operator but used when case sensitivity is important. If you wanted to make sure our output string “I got something!” always had the correct case, you can update the assertion like this:

It "should return 'I got something!'" {
    Get-Something | Should -BeExactly 'I got something!'
}

If someone updated our code to output “i got something!” with a lowercase i, then our Pester test would fail.

You can also use wildcards in our comparison along with specifying if it is case-sensitive or not with BeLike and BeLikeExactly. Note the differences in the capital “I” and lowercase “i”.

$string = "i got something!"
$string | Should -BeLike "I got *"  # Test will pass
$string | Should -BeLikeExactly "I got *"  # Test will fail

BeGreaterThan, BeGreaterOrEqual, BeLessThan, BeLessOrEqual

These operators will compare values for equality between objects. These use the common PowerShell operators for value comparison, like -gt, -ge, -lt, and -le.

For example, if you want to make sure the version is greater than a specific version:

[version]'2.0.1' | Should -BeGreaterThan ([version]'1.5.0')

Or just comparing number values:

10 | Should -BeGreaterOrEqual 10  # Test will pass
$Error.Count | Should -BeLessThan 1  # Hopefully this passes!
10 | Should -BeLessOrEqual 10  # Test will pass

BeTrue, BeFalse

Pretty straightforward, this operator tests if the value should be true or false. While the Test-NetConnection may not be practical, I wanted to should something else besides passing a variable to the Should statement.

$false | Should -BeFalse
0 | Should -BeFalse
$true | Should -BeTrue
1 | Should -BeTrue
# Testing Network Connection
Test-NetConnection -ComputerName 192.168.1.1 -InformationLevel Quiet | Should -BeTrue

Contain

Checks that a collection contains the value. Combine with the -Not operator to check for collection not containing a value.

'apple', 'banana', 'carrot' | Should -Contain 'banana'  # Test will pass
'apple', 'banana', 'carrot' | Should -Not -Contain 'lemon'  # Test will pass
1..100 | Should -Contain 44  # Test will pass
1..100 | Should -Contain 888  # Test will fail

FileContentMatch, FileContentMatchExactly, FileContentMatchMultiple

Check to see if a file contains the specified text and whether or not it should be case sensitive (Match vs. MatchExactly). MatchMultiple will check for the content of the file being tested as one string object so you can compare across multiple lines.

Let’s say I have a file in C:\temp/myFile.txt with the following content:

This is my test string in my file.
This is the second line in my file.

I could write single or multi-line tests to match content in the file:

'C:\temp\myFile.txt' | Should -FileContentMatch 'my test string'  # Test will pass
'C:\temp\myFile.txt' | Should -FileContentMatch 'in my file.*\n.*This is the'  # Test will fail, not multiline test
'C:\temp\myFile.txt' | Should -FileContentMatchMultiline 'in my file.*\n.*This is the'  # Test will pass, testing multiline

Note that the file path being passed in the pipeline is in quotes, otherwise you’ll get an error stating Cannot run a document in the middle of a pipeline.

Testing with the multiline operator is tricky, and I haven’t quite figured it out yet. At the end of this article, I wanted to include a test to ensure that my function has comment-based help but couldn’t quite get the syntax.

BeNullOrEmpty

Checks values for null or empty string. Like the other operators, this can be combined with -Not to indicate it should not be null or empty.

$null | Should -BeNullOrEmpty  # Test will pass
$null | Should -Not -BeNullOrEmpty

Because

Adds a message to the test failure message.

$myString = "this is my string"
$myString | Should -Be "this is my string" -Because "they are the same string"
$myString | Should -Be "not my string" -Because "the strings are not the same"

The second test will fail, and the message will appear in the test failure output:

Expected strings to be the same, because the strings are not the same, but they were different.
   Expected length: 13
   Actual length:   17
   Strings differ at index 0.
   Expected: 'not my string'
   But was:  'this is my string'
   at $myString | should -Be "not my string" -Because "the strings are not the same"

HaveParameter

This operator verifies parameters on functions and their associated properties, like making sure they are of the correct type and if they are mandatory. Use the Get-Command cmdlet to get the properties of the function.

# Verifies the command has a mandatory parameter named 'Name'
Get-Command Resolve-DnsName | Should -HaveParameter Name -Mandatory
# Example function
function Get-Something {
   [string]
   $MyString = "this is my string"
}
# Get-Something Tests, all should pass
Get-Command Get-Something | Should -HaveParameter MyString -Type String
Get-Command Get-Something | Should -HaveParameter MyString -DefaultValue "this is my string"
Get-Command Get-Something | Should -HaveParameter MyString -Not -Mandatory

Putting Pester Should Operators into Action

Let’s look at some additional assertions I added to our Pester test script from last time. While I focused on making sure my command had the correct output, you can also use Pester to verify our module is properly formatted. Here’s the function again, slightly updated from last time:

function Get-Something {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string]
        $ThingToGet = "something"
    )
    Write-Output "I got $ThingToGet!"
}

For example, you can make sure that our Get-Something function has the required -ThingToGet parameter, that it is of type String and has a default value of “something.” Here is a new Context block with those three assertions written into a single It block:

Context "testing parameter ThingToGet" {
  It "should have a parameter named ThingToGet" {
    Get-Command Get-Something | Should -HaveParameter ThingToGet -Type String
    Get-Command Get-Something | Should -HaveParameter ThingToGet -DefaultValue "something"
    Get-Command Get-Something | Should -HaveParameter ThingToGet -Not -Mandatory
  }
}

You can also write some assertions to ensure that the module has the Get-Something function defined in it. Within a Context or It block, you can run other commands to gather information before running the tests. In this case, you’ve already imported the module into the current session, so you can run Get-Module to get information about it, including exported functions.

Context "verify module has defined functions" {
  $moduleFunctions = (Get-Module PSSSomethingModule).ExportedFunctions.Keys
  
  it "should have a Get-Something function" {
    $moduleFunctions | Should -Contain 'Get-Something'
  }
}

The $moduleFunctions will have all the exported functions, and you use the -Contains operator to verify the Get-Something value is in it.

Here’s the updated Pester test script for the module:

Import-Module .\PSSomethingModule.psm1 -Force
Describe "Get-Something" {
    Context "verify module has defined functions" {
        $moduleFunctions = (Get-Module PSSSomethingModule).ExportedFunctions.Keys
        
        it "should have a Get-Something function" {
            $moduleFunctions | Should -Contain 'Get-Something'
        }
    }
    
    Context "testing parameter ThingToGet" {
        It "should have a parameter named ThingToGet" {
            Get-Command Get-Something | Should -HaveParameter ThingToGet -Type String
            Get-Command Get-Something | Should -HaveParameter ThingToGet -DefaultValue "something"
            Get-Command Get-Something | Should -HaveParameter ThingToGet -Not -Mandatory
        }
    }    
    
    Context "when parameter ThingToGet is not used" {
        It "should return 'I got something!'" {
            Get-Something | Should -Be 'I got something!'
        }
        It "should be a string" {
            Get-Something | Should -BeOfType System.String
        }
    }
    Context "when parameter ThingToGet is used" {
        $thing = 'a dog'
        
        It "should return 'I got' follow by parameter value" {
            Get-Something -ThingToGet $thing | Should -Be "I got $thing!"
        }
        It "should be a string" {
            Get-Something -ThingToGet $thing | Should -BeOfType System.String
        }
    }
}

This is what I meant by figuring out all the cool ways to test your code. It’s taking me a bit to figure out the different ways to test code, and the best thing is to look at other people’s examples. As the module gets more complicated, the more tests you can make.

Here’s our new output from running the updated test script:

Invoke-Pester -Output Detailed
PowerShell output of our updated Pester test script
PowerShell output of our updated Pester test script

Check out the code for the updated module and test script in my GitHub repo:
JeffBrownTech | pester-examples | 02-should-operators

Additional Reading:
Pester Docs: Performing Assertions with Should

Questions or comments? If so, drop me a note below or find me on Twitter or LinkedIn to discuss further.