Words: 830
Time to read: ~ 4 minutes
I’ve been working with Pester v5 lately.
Pester v5 with PowerShell v5 at work & Pester v5 with PowerShell Core outside of work.
There are quite a few changes from Pester version 3, so it’s almost like learning a new language… except it’s based on slang. I think that I’m speaking eloquently, and then I’ve suddenly insulted someone and Pester no longer wants to play nice with me.
Initial Tests
I’ve got the following data that I’m using to test Pester v5.
BeforeDiscovery -ScriptBlock {
$Groups = @(
[PSCustomObject] @{
Server = 1
Group = 'A'
Value = '86b7b0f9-996f-4c19-ac9a-602b8fe4d6f2' -as [guid]
},
[PSCustomObject] @{
Server = 1
Group = 'B'
Value = 'e02913f7-7dae-4d33-98c9-d05db033bd08' -as [guid]
},
[PSCustomObject] @{
Server = 2
Group = 'A'
Value = '96ad0394-8e9e-4406-b17e-e7d47f29f927' -as [guid]
},
[PSCustomObject] @{
Server = 2
Group = 'B'
Value = 'f8efa8b6-e21b-4b9c-ae11-834e79768fee' -as [guid]
}
)
}

Usually, I would only use -TestCases
to iterate through the data. I know that in Pester v3, I could wrap the It
blocks inside a foreach () {}
, and it would be okay. Hell, in most of my testings, it was faster. It doesn’t matter; I liked using -TestCases
, and the performance difference is negligible to me.
That is still an option with Pester v5. I can run the below code to confirm.
Describe -Name 'Attempt: 01' -Tag '01' -Fixture {
Context -Name 'Server: <_.Server>' -Fixture {
It -Name 'should have a guid for its value: <_.Value>' -TestCases $Groups {
$_.Value | Should -BeOfType [guid]
}
}
}

If I look at the data, I can see that I’ve got two different values for Server; 1 and 2. It would be great if I could group the tests by those server values.
For me, Pester has three main blocks; Describe
, Context
, and It
.
I know that Pester v5 has a -ForEach
parameter for each of these blocks. I’ve already tried using the -ForEach
parameter against the It
block, and it didn’t do what I wanted.

I’ll try it against the Context
block instead and see if it works.
Describe -Name 'Attempt: 02' -Tag '02' -Fixture {
Context -Name 'Server: <_.Server>' -Foreach $Groups {
It -Name 'should have a guid for its value: <_.Value>' -Test {
$_.Value | Should -BeOfType [guid]
}
}
}

That kind of works but we’ve got the same server in two different groups. Let’s move the groups up to the Describe level.
Describe -Name 'Attempt: 03 - Server: <_.Server>' -Tag '03' -Foreach $Groups {
Context -Name 'Server: <_.Server>' -Fixture {
It -Name 'should have a guid for its value: <_.Value>' -Test {
$_.Value | Should -BeOfType [guid]
}
}
}

We’ll that’s not what I wanted. Instead of 1 describe block, we have multiple blocks; 1 per group.
Grouped Data
Now, I’m going to start using Group-Object
. My data by itself doesn’t seem to work.
$Groups = @(
[PSCustomObject] @{
Server = 1
Group = 'A'
Value = '86b7b0f9-996f-4c19-ac9a-602b8fe4d6f2' -as [guid]
},
[PSCustomObject] @{
Server = 1
Group = 'B'
Value = 'e02913f7-7dae-4d33-98c9-d05db033bd08' -as [guid]
},
[PSCustomObject] @{
Server = 2
Group = 'A'
Value = '96ad0394-8e9e-4406-b17e-e7d47f29f927' -as [guid]
},
[PSCustomObject] @{
Server = 2
Group = 'B'
Value = 'f8efa8b6-e21b-4b9c-ae11-834e79768fee' -as [guid]
}
)

We can pass that data into Group-Object
to group our data by a certain property. In my case, I want to group the data by the Server
property.
$Groups | Group-Object -Property Server

Taking a look at the first group, I only have the data for that single property value.
($Groups | Group-Object -Property Server)[0].Group

Now, I’ll try the Pester code again.
Grouped Tests
First, I’ll try putting the groups into the It
blocks and see if that works.
Describe -Name 'Attempt: 05' -Tag '05' -Fixture {
BeforeDiscovery -ScriptBlock {
$GroupedGroups = $Groups | Group-Object -Property Server
}
Context -Name 'Server: <_.Name>' -Fixture {
It -Name 'should have a guid for its value: <_.Group.Value>' -ForEach $GroupedGroups {
$_.Group.Value | Should -BeOfType [guid]
}
}
}

It doesn’t fully work. The data is grouped but the results seems to be concatenating the values. I’d like it better if they were split out to separate tests per value.
This time, I’ll group the data in the context blocks and then pass the groups into the It
blocks. I’ll do this by passing the groups into the -ForEach
parameter of the It
block using $_.Group
.
Describe -Name 'Attempt: 04' -Tag '04' -Fixture {
BeforeDiscovery -ScriptBlock {
$GroupedGroups = $Groups | Group-Object -Property Server
}
Context -Name 'Server: <_.Name>' -ForEach $GroupedGroups {
It -Name 'should have a guid for its value: <_.Value>' -TestCases $_.Group {
$_.Value | Should -BeOfType [guid]
}
}
}

In the previous code blocks, I used the BeforeDiscovery
block in the Describe
block. If you don’t want to use that, you can pass the Group-Object
cmdlet to the ForEach
parameter as a subexpression.
Describe -Name 'Attempt: 06 - Server: <_.Name>' -Tag '06' -ForEach ($Groups | Group-Object -Property Server) {
Context -Name 'Server: <_.Name>' -Fixture {
It -Name 'should have a guid for its value: <_.Value>' -TestCases $_.Group {
$_.Value | Should -BeOfType [guid]
}
}
}

Pass or Fail
I’ve encountered this obstacle of grouping objects in tests a couple of times. I’m hoping that by writing this down, I’ll be able to commit the information to memory.
Hey, if it doesn’t, I can always grab the code and figure it out.
2 thoughts on “Pester 5 and Group-Object – Best Friends”