Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Discovering Pester Tags with the PowerShell AST

Posted on January 5, 2022January 5, 2022

As I resolved at the end of last year, I am doing more with Pester in 2022. I'm getting a bit more comfortable with Pester 5 and as my tests grow in complexity I am embracing the use of tags. You can add tags to different Pester test elements. Then when you invoke a Pester test, you can filter and only run specific tests by their tag. As I was working, I realized it would be helpful to be able to identify all of the tags in a test script. After a bit of work, I came up with a PowerShell function.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

My initial thought was to use regular expressions and look for lines with -tag.

$path = "C:\scripts\psfunctiontools\tests\psfunctiontools.tests.ps1"
[regex]$rx="(?<=-tag\s)(\w(,)?(\s)?)*"
$c = Get-Content $path
#make all tags lower case
([System.Text.RegularExpressions.Regex]::Matches($c,$rx,"IgnoreCase")).foreach({($_.value -split ",").Trim().toLower()}) |
Select-Object -Unique | Sort-Object

This code works but is a bit of a brute-force approach. I had to take into account multiple tags and syntax variations. So I went back to the PowerShell AST.

Parsing the file is simple enough.

$AST = [System.Management.Automation.Language.Parser]::ParseFile(
    $path,
    [ref]$astTokens,
    [ref]$astErr
)

In a Pester test, tags are technically part of a -Tag parameter for a Pester command like Describe or It. This means I can discover these command elements.

$tags = $AST.FindAll({
    $args[0] -is [System.Management.Automation.Language.CommandParameterAst] -AND $args[0].parametername -eq 'tag' },
$true
)

Now for the tricky part. Here's a sample object.

sample AST tag object

It would be nice if the Argument property contained the values "help", and "acceptance" which you can see in the parent. But that would be too easy. However, I can use the Parent property and its CommandElements.

tag command elements

I'm showing you the last two elements. The Tag parameter value is the element after the parameter element. This might make it clearer.

for ($i = 0;$i -lt $tags[0].Parent.CommandElements.count;$i++) {
    if ($tags[0].parent.CommandElements[$i].parametername -eq 'tag') {
    $tags[0].parent.CommandElements[$i+1].extent.text.split(",").trim().tolower()
    }
}

I can loop through all the command elements using a For enumeration loop. If the parametername property is 'tag', then get the next element ($i+1) and use the Extent.Text property. It might be an array so I'll split it and trim up extra spaces.

get tag value

All I need to do now is step back and do this for all tag elements with a set of nested For loops.

get all tag values

The last step is to get unique items. Notice that I'm also making every entry lowercase to ensure I'm truly getting unique values.

Get-PesterTag

With this core functionality, I built a PowerShell function around it.

Function Get-PesterTag {
    [cmdletbinding()]
    [OutputType("pesterTag")]
    Param(
        [Parameter(
            Position = 0,
            Mandatory,
            HelpMessage = "Specify a Pester test file",
            ValueFromPipeline
        )]
        [ValidateScript({
            #validate file exits
            if (Test-Path $_) {
                #now test for extension
                if ($_ -match "\.ps1$") {
                    $True
                }
                else {
                    Throw "The filename must end in '.ps1'."
                }
            }
            else {
                Throw "Cannot find file $_."
            }
        })]
        [string]$Path
    )

    Begin {
        Write-Verbose "[$((Get-Date).TimeofDay) BEGIN  ] Starting $($myinvocation.mycommand)"
        New-Variable astTokens -Force
        New-Variable astErr -Force
    } #begin

    Process {
        Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Getting tags from $Path "
        $AST = [System.Management.Automation.Language.Parser]::ParseFile(
            $path,
            [ref]$astTokens,
            [ref]$astErr
        )
        $tags = $AST.FindAll({
                $args[0] -is [System.Management.Automation.Language.CommandParameterAst] -AND $args[0].parametername -eq 'tag' },
            $true
        )

        $all = for ($j = 0; $j -lt $tags.count; $j++) {
            for ($i = 0; $i -lt $tags[$j].Parent.CommandElements.count; $i++) {
                if ($tags[$j].parent.CommandElements[$i].parametername -eq 'tag') {
                    $tags[$j].parent.CommandElements[$i + 1].extent.text.split(",").trim().tolower()
                }
            }
        }
        if ($all) {
            [pscustomobject]@{
                PSTypename = "pesterTag"
                Path       = $Path
                Tags       = $all | Select-Object -Unique | Sort-Object
            }
        }
        else {
            Write-Warning "No tags found in $Path"
        }
    } #process

    End {
        Write-Verbose "[$((Get-Date).TimeofDay) END    ] Ending $($myinvocation.mycommand)"
    } #end

} #close Get-PesterTag

The function writes a custom object to the pipeline which includes the file name.

get-pestertag

This was very helpful as I discovered a misspelled tag name. The function includes parameter validation on the path. The validation script first verifies the file exists and then that it ends in .ps1. I used to separate these out into two separate validation tests, but there's no reason not to combine them. The only other thing I could do is create a custom format file for the pesterTag object type. But I'll leave that to you. I need to get back to working on my tests.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

3 thoughts on “Discovering Pester Tags with the PowerShell AST”

  1. Pingback: Discovering Pester Tags with the PowerShell AST - The Lonely Administrator - Syndicated Blogs - IDERA Community
  2. Pingback: Discovering Pester Tags Revisited • The Lonely Administrator
  3. Pingback: Discovering Pester Tags – Curated SQL

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d