Few days ago I posted about a tiny module I wrote to skip the entry point function in a script. I got few reactions telling me that there are better ways to organize your scripts, and they were all correct. Putting your code into a module and distributing it that way, or splitting the script into different files and combining them during build are both better than having a single file with everything.

The post was not a recommendation how to write your scripts all the time, instead it just described a technique that you can use when needed. One such case is when you are providing scripts to others that do not know PowerShell. Providing a single self-contained script is simpler than distributing, importing and invoking a module. I also find it useful for build scripts, or scripts in scheduled tasks.

Testing entry point function invocation

There are probably other possible use cases, but this is the one I am using this module for. To test that parameters are passed correctly to the entrypoint function. In my case the entrypoint function does an involved, and destructive action, so I cannot simply invoke the whole script and check result, without setting up the whole environment for it.

Below I will show the same thing on a function that has non-destructive behavior, so a better way to test it would be to simple invoke the whole script and check the result. So keep that in mind.

# file script1.ps1 [CmdletBinding(DefaultParameterSetName="SelectOne")] param ( [Parameter(ParameterSetName="SelectOne", Mandatory)] [ValidateSet("Avocado", "Orange", "Pear")] $Name, [Parameter(ParameterSetName="Random")] [Switch] $Random ) function Get-Fruit { [CmdletBinding(DefaultParameterSetName="SelectOne")] param ( [Parameter(ParameterSetName="SelectOne", Mandatory)] [ValidateSet("Avocado", "Orange", "Pear")] $Name, [Parameter(ParameterSetName="Random")] [Switch] $Random ) $emojis = @{ "Avocado" = "🥑" "Orange" = "🍊" "Pear" = "🍐" } if ($Random) { $k = Get-Random -Collection ($emojis.Keys) return $emojis[$k] } $emojis[$Name] } # the next line is very hard to test Get-Fruit @PSBoundParameters

Our Get-Avocado function was renamed to Get-Fruit and now can return different emojis, or a random one. I am invoking it from the script and passing some mandatory parameters, in two parameter sets.

# file script1.Tests.ps1 $script = "$PSScriptRoot/script1.ps1" Import-Script ` -EntryPoint Get-Fruit ` -Parameters @{ Random = $true } ` -Path $script Describe "Pass the parameters to the entry point function" { It "Passes Random" { Mock Get-Fruit & $script -Random Assert-MockCalled Get-Fruit ` -ParameterFilter { $true -eq $Random } } It "Passes Name" { Mock Get-Fruit & $script -Name Avocado Assert-MockCalled Get-Fruit ` -ParameterFilter { "Avocado" -eq $Name } } } Describe "Get-Fruit" { It "gets avocado" { Get-Fruit -Name Avocado | Should -Be 🥑 } It "gets orange" { Get-Fruit -Name Orange | Should -Be 🍊 } It "gets pear" { Get-Fruit -Name Pear | Should -Be 🍐 } }

In the test I am then importing the script, passing in the Random switch, to make sure I am able to run the script. The call to Get-Fruit is still replaced by the mock so I am able to import the script without ever invoking Get-Fruit .

At that moment I have the Get-Fruit imported to my local scope and can mock it. Which in turn means that I can test just the param block on the top of the script.

Summary