If you want to ensure commands inside of your PowerShell scripts execution in a specific order, build Pester tests to ensure they do.

Be sure to check out the Pester Book to learn how to not only learn how to use Pester but how to learn testing methodologies!

One of the great features of Pester is mock assertions. If I have a script or a function that contains various function references inside, and I want to be sure my code follows a particular pattern, I can create mocks for those functions and then use the Assert-MockCalled command to do so.

For example, let's say I have a function that first tests to ensure a computer is online. If it's available, I'll then create a file on it.

function New-ExampleFunction
{
    param (
      [Parameter(Mandatory)]
      [string]$ComputerName,
 
      [Parameter(Mandatory)]
      [string]$FileName
   )
 
   if (Test-Connection -ComputerName $ComputerName -Quiet -Count 1)
   {
      Add-Content -Path "\\$ComputerName\c$\$FileName" -Value 'some value here'
   }
}

Typical Pester Assertions

I can then create a couple of Pester tests to ensure the computer was checked to be available, and I attempted to create the file on the computer.

describe 'New-ExampleFunction' {
 
   mock 'Test-Connection' {
      $true
   }
 
   mock 'Add-Content'
 
   it 'tests to ensure the computer is online' {
 
      New-ExampleFunction -ComputerName 'somecomputer' -FileName 'somefilename.txt'
 
      $assMParams = @{
         'CommandName' = 'Test-Connection'
         'Times' = 1
         'Exactly' = $true
         'Scope' = 'It'
         'ParameterFilter' = {$ComputerName -eq 'somecomputer' }
      }
      Assert-MockCalled @assMParams
   }
 
   it 'attempts to create the right file' {
 
      New-ExampleFunction -ComputerName 'somecomputer' -FileName 'somefilename.txt'
 
      $assMParams = @{
         'CommandName' = 'Add-Content'
         'Times' = 1
         'Exactly' = $true
         'Scope' = 'It'
         'ParameterFilter' = { $Path -eq '\\somecomputer\c$\somefilename.txt' }
      }
      Assert-MockCalled @assMParams
   }
 
}

This is all well and good but this time order is important to me. As-is, I could have accidentally copied and pasted the Test-Connection snippet after the Add-Content reference and Pester would gladly display all green for my test results. This would defeat the purpose of my function altogether. Pester currently has no idea of the order of execution. Let's fix that.

Unfortunately, there's no built-in way to test order of function execution. However, since this is just PowerShell, we can build our own! In a nutshell, we're going to build out everything the same but this time I'm also going to create an array that each mock will add an item to when it's called. Once I have this array, I can then assert which indexes in that array should be what values. This will allow me to check various index values.

Testing Assertions in Order

Using the above example again, let's write these tests to ensure a particular order; that is to ensure Test-Connection is called before Add-Content.

First, I'll create an array called mockOrder at the top of my describe block. You'll notice that I'm scoping it to script because otherwise, the mocks wouldn't be able to see it.

$script:mockOrder = @()

Next, I'll modify the mocks to add some descriptive value to the array if they are executed.

mock 'Test-Connection' {
   $true
   $script:mockOrder += 'Test-Connection'
}
 
mock 'Add-Content' {
   $script:mockOrder += 'Add-Content'
}

Now I know whenever Test-Connection or Add-Content is called in my function, they will add their name as a value to the $mockOrder array.

I now don't even need to use Assert-MockCalled at all. At this point, I simply need to check a couple of values in the $mockOrder array to ensure they are in the right place.

$script:mockOrder[0] | should be 'Test-Connection' $script:mockOrder[1] | should be 'Add-Content'

This is asserting that the Test-Connection value is the first element in the array thus was called first, and Add-Content is right after it.

Let's intentionally break it by switching up the order.

function New-ExampleFunction
{
   param (
   [Parameter(Mandatory)]
   [string]$ComputerName,
 
   [Parameter(Mandatory)]
   [string]$FileName
)
 
   Add-Content -Path "\\$ComputerName\c$\$FileName" -Value 'some value here'
   Test-Connection -ComputerName $ComputerName -Quiet -Count 1
}

Original pester test:

2016-08-09_18-01-17

Now I'll refactor the tests to check for order as I've explained above.

2016-08-09_18-03-27

Nice! It failed. I can then get it to pass again by putting the function references in the right order.

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!