Try Now

Introduction to Pester – Part 1

Table of Contents

Post Featured Image

Code review and software unit tests are not the most popular tasks in code development and PowerShell DevOps are not spared. Still, when you’re writing a function or a script to perform a specific task, it’s essential to know whether your script does what it’s supposed to do.

With the Pester framework for PowerShell testing and mocking, this task becomes less time-consuming and annoying. Pester provides a language for writing unit and integration tests for your PowerShell scripts, cmdlets, functions and modules. And as we are used to from PowerShell, these tests can be easily automated. 

In this article you will learn how to get started with Pester as well as the basics of using Pester to test your scripts.

 

Installing Pester

First you must check if Pester is available on your computer and also its version.
Get-Module -ListAvailable Pester

Check the output and it will state the version of Pester that’s installed here. The following output shows that Pester is available and the version is 5.0.4.
Directory: C:\\Program Files\\WindowsPowerShell\\Modules
ModuleType Version Name ExportedCommands
---------- ------- ---- ----------------
Script 5.0.4 Pester {Invoke-Pester, Describe, Context, It...}

If the output shows a version of Pester which is lower than 5, you must update it using:
Update-Module -Name Pester

Use the following command to install Pester:
Install-Module Pester -Force

Now you are ready to import the module:
Import-Module Pester

Now, when you run the command to check for the imported module, you should be able to view Pester (as can be seen in figure 1):
Get-Module
Screenshot: Powershell console displaying details on the Pester PowerShell module

Fig. 1: Pester is now visible in the modules output

 

Creating Pester Tests

Let’s create your first Pester test. We will look at the first example here. We will break this down into a few parts. Let’s suppose that you need to test a script created to perform certain tasks in Office 365. This will require you to connect PowerShell to your Office 365 tenant. Please check this link to download the EXO V2 PowerShell Module.

Use these commands to connect to Office 365 tenant:

$Credentials = Get-Credential
Connect-ExchangeOnline -Credential $Credentials

You have a csv file with the list of the users on whom you need to perform your tasks. The first aim would be to ensure that the path that’s provided does actually exist. Let’s create a test to verify that.

Describe "Test-CSV" {
It "CSV exists" {
"" | Should -Exist
}
}

If the file exists in the said location, then the output should resemble this:

Screenshot: PowerShell output depicting that the path test was successful

Fig. 2: Successful path test

There are quite a few things happening here. First thing is, we used the Invoke-Pester command to start the Pester test. This is the way to call upon a test.

The next part is to discover the test file on the given location. Pester tests have this format “filename.tests.ps1″.

Tip: ScriptRunner automatically recognizes Pester tests by their file format. The corresponding test scripts are automatically filed in the “Pester Tests” sublibrary of the ScriptRunner Admin App.

Screenshot: ScriptRunner Admin App

ScriptRunner automatically sorts Pester scripts in the “Pester Tests” sublibrary.

Once the file is found it would check the number of conditions given and then perform the test. The test result is then displayed at the bottom.

In this case, I had created the file at the location mentioned; hence, my test was successful. However, if the file isn’t found at that location, the output would be as shown in figure 3:

PowerShell output depicting that the path test was unsuccessful

Fig. 3: Unsuccessful Path Test

The other aspects of this test are the ‘Describe’ and ‘It’ blocks. You will read more about them in the next section.

 


Pester Blocks

The functions used in Pester are easy to grasp as seen in the example discussed earlier. The blocks used in Pester are:
  • Describe
  • It
  • Context
  • BeforeAll
  • AfterAll
  • BeforeEach
  • AfterEach

Describe Block

This is a script-block in PowerShell. You will enter almost all your test code in this block. The blocks It, Context and others can all be added within it.

Describe serves as a group of tests. You can add multiple Describe blocks in your test.

Here’s an example: We will build upon the following script over the course of this article. I have also included the script in its final form later in this post.

function Get-UsageLocation ($User){
return (Get-Mailbox $User).UsageLocation
}
Describe "Retrieve Mailbox Information" -Tag 'MailboxInfo' {
It "Check if India" -Tag "India" {
Get-UsageLocation "yogi@testdomain1988.onmicrosoft.com" | Should -Be "India"
}
It "Usage location should not be UK" -Tag "NotUK" {
Get-UsageLocation "derek@testdomain1988.onmicrosoft.com" | Should -Not -Be "United Kingdom"
}
}

You would be testing two possibilities using Describe. The mailbox “derek@testdomain1988.onmicrosoft.com” has usage location as “United Kingdom”; hence the test will fail in the 2ndIt block.


It Block

The It block is where you state your conditions against which the test shall run. As seen in the example above, we provided the condition that the usage location of the mailbox must be “India” in the first It block.

The 2nd It block had the condition that the usage location must not be “United Kingdom”.

The result of the test can be one of the following:

  • Passed – All the conditions in the test passed.
  • Failed – The result did not meet the conditions set by us.
  • Skipped – The test was skipped.
  • Pending – The test could not be run as its pending or empty.
  • Inconclusive – Result of the test was neither passed nor failed.

In our example the 1st It block test passed, whereas the 2nd one failed (figure 4).

Screenshot: PowerShell output depicting that the first it block test succeeded while the second failed

Fig. 4: It block test output


Context Block

The Describe block groups all the tests within it. However, if a test needs to be run in different conditions or circumstances, you can use the Context block. This block is similar to the Describe block; however, there are a few significant differences.

The Context block will exist within the Describe block. There can even be multiple Context blocks in one Describe block: As the name suggests, you could use them to divide your tests in different contexts.

In the example here, you will notice that I have added 2 Context blocks to differentiate between the tests for usage location and address book. Here’s the code:

Context "Check Usage Location" {
It "Check if India" -Tag "India" {
Get-UsageLocation "yogi@testdomain1988.onmicrosoft.com" | Should -Be "India"
}
It "Usage location should not be UK" -Tag "NotUK" {
Get-UsageLocation "derek@testdomain1988.onmicrosoft.com" | Should -Not -Be "United Kingdom"
}
}
#end of usagelocation context
Context "Is it hidden from the address book" -Tag HiddenFromGAL {
It "Check hidden from GAL attribute" {
Get-HiddenFromGal "vignesh@testdomain1988.onmicrosoft.com" | Should -Be "False"
}
} #end of Hidden from GAL context block

BeforeAll Block

You might want to run some specific tasks or codes at the beginning. In our example, there are 2 functions at present which need to be referred to later in the script. One option is to use the BeforeAll block to save the 2 functions in memory.

This block is run before any of the It blocks.
You can now add the functions within the BeforeAll block as shown here:

BeforeAll {
#Connecting to Exchange Online PowerShell Module
. #Functions
function Get-UsageLocation ($User){
return (Get-Mailbox $User).UsageLocation
}
function Get-HiddenFromGal ($Account){
return (Get-Mailbox $Account).HiddenFromAddressListsEnabled
}
}

Running the test now clearly shows that its functioning as expected (see figure 5).

Screenshot: PowerShell output depicting the output of BeforeAll block

Fig. 5: BeforeAll test output


AfterAll Block

In certain situations, you might want to perform a set of tasks at the end of a test or block. Hence, it’s run after the execution of all the It blocks.

Adding the following code block at the end of the last It block will ensure that the Exchange Online PowerShell session is disconnected after the tests have run:

AfterAll{
Disconnect-ExchangeOnline -Confirm:$false
}

BeforeEach/AfterEach Block

The BeforeEach and AfterEach blocks come in handy when you wish a certain code block to be executed before and after every It block. You can use it to make an entry to a log file or to even display something in the PowerShell result.

In the example that we have been building on in this session, I have used the BeforeEach and AfterEach blocks to show a text in the result:

BeforeEach {
Write-Host "BeforeEach Block" -BackgroundColor DarkMagenta
}
AfterEach {
Write-Host "AfterEach Block" -BackgroundColor Black
}

 

Conclusion

As you have seen, the basics of Pester are easy to learn and you can quickly write your first simple unit tests.
For more complex tests it is necessary to consider references to other cmdlets and functions. This is where mocking comes in. Stay tuned to find out more about mocking, assertions and tags in the second part of this article.

About the author: