Testing a self-contained script file with Pester is difficult. I visited this idea few times before, for example here and never reached a solution that I would like, but every time I am getting closer and closer.

What is a self-contained script?

A self-contained script is a script that does some work when you run it by invoking functions it defines. For example saving the following code in script1.ps1 file and running it, would return 🥑.

function Get-Avocado { '🥑' } Get-Avocado

Why is this a problem?

Testing such script with Pester is difficult, because the Get-Avocado function is invoked every time the script is dot-sourced into the test script.

. $PSScriptRoot/script1.ps1 Describe "Get-Avocado" { It "gets avocado" { Get-Avocado | Should -Be 🥑 } }

# output 🥑 <--- this should not be here, and comes from dot-sourcing the script Describing Get-Avocado [+] gets avocado 93ms

In this case it is not the end of the world, all you get is a bit of extra output to the screen.

In the real world though, the entry point function to your script will do a lot more than just write to a screen, and this extra execution will become a problem.

Making it better

To successfully cover this script with tests we need to skip running the entry point function. This can be done in multiple ways, but the best way I came up with so far is “shadowing” the entry point function with a temporary alias. Aliases are resolved before functions, and are defined accross script boundaries, so effectively we replace the call to Get-Avocado with call to function f that does nothing.

In code that would look like this:

function f () {} New-Alias -Name Get-Avocado -Value f . $PSScriptRoot/script1.ps1 Remove-Alias -Name Get-Avocado Remove-Item "function:/f" Describe "Get-Avocado" { It "gets avocado" { Get-Avocado | Should -Be 🥑 } }

# output <--- no extra output here Describing Get-Avocado [+] gets avocado 93ms

Making it awesome

This approach is simple and clear, but providing it as a reusable solution would be much better. So I did just that, and created ImportTestScript module and published it to PSGallery.

Import-Script ` -EntryPoint Get-Avocado ` -Path $PSScriptRoot/script1.ps1 Describe "Get-Avocado" { It "gets avocado" { Get-Avocado | Should -Be 🥑 } }

# output <--- no extra output here Describing Get-Avocado [+] gets avocado 93ms

As you can see I am calling the Import-Script function and providing it with the path to the script to import, and the name of the entry point function. The default value here is Main , but you can choose whatever suits your needs.

Doing the outlined operations from the inside of a module is a bit more involved, but you can find it described here in this gist.

Summary

Shadowing functions with aliases is a great technique that can be used to test scripts that invoke the functions that they define. Following a very simple rule of calling just a single function to start the whole script, will allow you to replace it easily with the provided module and test as much as you can from your script.