I recently saw a post by Jakub Jares on testing self-contained scripts with Pester.

He suggests, essentially, hijacking the call to any functions, dot-sourcing the script, and then removing the hijack (an alias, in this case).

We do something similar at work to test Azure runbooks - containing all actual code that runs in a given script in an entry-point function called something similar to Start-Main, replicating all parameters for the script, and then hijacking it for testing.

I was playing with the AST a while ago, and quite like this alternate approach:

Get-ScriptFunctions -ScriptPath "$PSScriptRoot/script1.ps1" -Export | Invoke-Expression

Describe "Get-Avocado" {
    It "gets avocado" {
        Get-Avocado | Should -Be 🥑
    }
}

OK, so we're missing some context here - specifically, what is Get-ScriptFunctions (other than badly named), and why do I like it more:

As you can see, it's a quick attempt to use the language parser to find all function definitions in a given file (or scriptblock) and output them - which you can pipe to Invoke-Expression in order to import the function definitions into your current session.

I've tried to add it to a few different places, including PowerShellTemplate (where it can be used to quickly convert a monolithic function-filled PSM1/PS1 file into my preferred single-function-per-file layout). I should probably find the best / most up-to-date version and update it everywhere / put it into a small module and add dependencies, as I seem to be diverging.

Anyway, I prefer this to other approaches listed for a few reasons -

  • It doesn't require you to know / list all calls in the main script
  • There's no risk of unexpectedly running actual code
  • You don't have to contain all run-time code in a function (and replicate huge parameter blocks)

But there's probably something I haven't considered that makes this less suitable - please let me know if I've missed something obvious!