Is there a UNION in PowerShell

Words: 462 487

Time to read: ~ 3 minutes

TL;DR: [System.Collections.Generic.HashSet<T>]

It has been a while ago since I’ve blogged so it seems fitting that this post will be about a question that was asked of me a while ago. The question was along the lines of “Can I join objects in PowerShell but remove duplicates?”.

So this is allowed:

1
2
3
4

But this isn’t allowed:

1
2
3
1

For me, it summed down to “Is there something like UNION or UNION ALL in PowerShell?

Luckily, this is something that I had asked before and been told the answer. So here I am, repeating the answer for you all since this is how I learn; repetition and practice.

The PowerShell type:

[System.Collections.Generic.HashSet<T&gt;]
Don’t worry about the ‘<T>’. I didn’t figure it out either but I’ve been informed that it means a generic Type.

UPDATE: I have been reliably informed that this is a major understatement and I will update when I know more/learn more/am taught more about this.

https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1?view=netframework-4.7.2

An Example, not a Speech.

Let’s say we have similar but different objects.

$Boom = 1
$Blast = 2
$And = 3
$Ruin = 4

Now the question that we are asked is, if there is anyway to do a UNION on these objects?

Absolutely, we’ll create a hashset object and start putting these objects into the HashSet. I know these objects are integers so I’m going to put [Int] in for the <T> type.

#Create the HashSet object.
$HashSet = [System.Collections.Generic.HashSet[Int]]::new()

#Add the objects.
foreach ($Item in $Boom,$Blast,$And,$Ruin) {
    $HashSet.Add($Item)
}
Verbose by default

Now when you add something to a HashSet using the .Add method it returns either a True or a False.

Returns
Boolean
true if the element is added to the HashSet<T> object; false if the element is already present.

https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.hashset-1.add?view=netframework-4.7.2#System_Collections_Generic_HashSet_1_Add__0_

If we then check the HashSet object, it returns both of our objects.

#Show us what you GOT!!!
$HashSet
and a 1 and a 2 and a…

If you don’t want to see the output of the .Add() method then you can push the output to one of the nulls e.g. $null = , [void] , > $null, | Out-Null, with the first two being placed at the start of the line and the last two at the end.

# Clear the HashSet.
$HashSet.Clear()

# Add items and hide output.
$null = foreach ($Item in $Boom,$Blast,$And,$Ruin) {
    $HashSet.Add($Item)
}

# See if it still worked.
$HashSet
Hide and seek…

Check for Duplicates.

Now let’s see what happens if we try to add a duplicate object to the HashSet.

#Clear the HashSet
$HashSet.Clear()

#Populate it again.
foreach ($Item in $Boom,$Blast,$And,$Boom){
    $HashSet.Add($Item)
}

#Check it again
$HashSet
No I don’t want no duplicates

HashSet sees the duplicate value, gracefully says no (False), and does not add it.

But wait, there’s more!

HashSet is something that I think DBAs will like as it is based on Sets. If you have the time, check out some of the other methods that it has.

#What else you got?
Get-Member -InputObject $HashSet
A set that contains everything except itself.

Take a look!

When does a Failing Pester Test return Green?

Words: 716
Time to read: ~ 3.5 minutes
TL;DR: Don't nest It blocks

I’ve been working more with different people in different departments more and more lately at work.

“Great” you might say and I would agree with you.

This has meant that any and all scripts that I write to give to these departments have to be tested so that they don’t fail when they are run.

“Great” you might say and, again, I would agree with you.

New-Fixture

For PowerShell scripts, I’ve been using Pester. When I want to write a new function, I run a Pester command to create a .ps1 and a .Tests.ps1 file.

It’s a simple enough setup – I checkout a new branch for the command and switch to it.

git checkout -b NewFunction

I know that Pester has a command that I can use to create a framework of a new function and a framwork of a Pester test file for that function.

Get-Command -Verb New -Module Pester |
    Where-Object Definition -like '*create*scripts*tests*'

This returns the command New-Fixture and a quick scan of the help shows that this is exactly what we are looking for.

(Get-Help New-Fixture).Synopsis

This function generates two scripts, one that defines a function
and another one that contains its tests.

(Get-Help New-Fixture).Synopsis

TDD

An effect of working with more and more departments is that I have less and less time to spend on these functions. That’s meant that I’ve had to take a hard look at what would be a “nice to have” and a “must have”.

Because of this I’ve spent more time on planning and writing the tests first. Then I write my scripts to pass these tests and only these tests.

If it is a “must have” then it gets a test. If it has a test then the script gets that functionality, or property, or parameter, etc.

If it’s a “nice to have” then it doesn’t get a test. If it doesn’t get a test then it’s not in the script. When I have time later then I’ll go back and see if I’ll add it

I have yet to have time to go back and add stuff but then I’ve yet to have a need to add any of the “nice to have”s.

You may think that thinking about what’s needed and writing the tests takes time and you’d be correct.

But anytime spent on the planning part is saved by not spending time writing code that isn’t needed and getting it to work. Overall, it’s a major time saving technique.

Back on Track

Here is what is in the .Tests.ps1 file created from the New-Fixture command.

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '.Tests.', '.'
. "$here\$sut"
Describe "<function name>" {
It "does something useful" {
$true | Should -Be $false
}
}

Contents of .Tests.ps1 file

It’s a nice template and all you need to do is modify that It block to have your test instead.

Here is where my Pester test failed and returned a green.

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"

Describe "Test-FakeFunction" {
    It "does something useful" {
        It 'should pass by deafult' {
            $true | Should -Be $false
        }
    }
}

It has a helpful message for you…

Do you see it?

The Reason

In modifying the template, I forgot to take out the original It block and just put my It block inside it.

This lead to my block “pass by deafult” failed
(as it should cause of that typo) but the original, parent block it was in “does something useful” passed!

It surprised me that I had a passing and failing tests since I thought I had only written 1 test but the maintainers of Pester included a helpful little error message for us.

You are already in a test case.

In the error message…3 times

Further Action

I’m an advocate of “if you see an issue, raise it on Github”. Even if you don’t fix the issue yourself, somebody else more than likely will come along and fix it for everyone.

Am I going to raise an issue for this on Github though?

  • An issue where it was due to my bad typing?
  • An issue where they already have it raised in the error message?
  • An issue where I accidentally put an It block inside an It block when I shouldn’t have?

For this instance, I think we’re okay…

Pester Testing Self Contained Scripts

Words: 1009

Time to read: ~ 5 minutes

Update:
    2019-02-22: Added a test for Jakub’s (don’t freak out, don’t freak out) comment.
    2019-06-13: Check out Jakub’s post on the topic – Such elegance http://jakubjares.com/2019/06/09/2019-07-testing-whole-scripts/

Continue reading “Pester Testing Self Contained Scripts”