Martin K
Martin K

Reputation: 193

Pester: How do I keep my script from running?

I am testing a PowerShell script. I'd like to test individual functions without executing the entire script. I am not sure if this is the intended use case or supported, and I am not finding a good answer searching online

sut.ps1:

Param(
    [Parameter(Mandatory = $true,
        Position = 0)]
    [ValidateNotNullOrEmpty()]
    [string] $Message
)

function fut([string] $argument) {
    Write-Host $argument
}

function Main() {
    fut($Message)
}

Main

sut.tests.ps1:

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

Describe "fut" {
    It "does something useful" {
        fut 'beer'
    }
}

Output

Message
Describing fut
beer
 [+] does something useful 385ms
Tests completed in 385ms

Now I can mock away 'beer', because it runs in the describe block. But I cannot mock away 'message', because the script starts to execute when I dot-source it.

In my case, 'message' is an operation I do not want to execute.

I am on Windows 10 15063.

Upvotes: 6

Views: 1447

Answers (3)

meliasson
meliasson

Reputation: 126

If you, for some reason

  • want to keep all functionality in a single script (as opposed to in a module or split into multiple files)
  • need the functionality to be executed when the script is dot sourced
  • want to test the script's functionality using Pester
  • don't mind adding some testability enhancing logic to the script

one approach is to add logic that skips auto-executing the script's functionality when a switch parameter is supplied.

I use something like that to keep my Azure Automation runbooks simple (simplicity is debatable; here "simple" translates to "keeping all functionality in a single script file") and testable:

SingleScript.Tests.ps1

BeforeAll {
    . $PSScriptRoot/SingleScript.ps1 -InTestContext
}

Describe 'SingleScript' {
    Context 'Invoke-Functionality' {
        It 'invokes script functionality' {
            $result = Invoke-Functionality
            $result | Should -Be 'Something useful'
        }
    }
}

In the test file above, within the BeforeAll block, auto-execution when dot sourcing is skipped with the switch parameter InTestContext.

SingleScript.ps1

param(
    [switch]
    $InTestContext
)

function Invoke-Functionality {
    'Something useful'
}

if (-not $InTestContext) {
    Invoke-Functionality
}

In the script file above, you can see the logic that allows skipping auto-execution.

Upvotes: 2

MoppleIT
MoppleIT

Reputation: 1

This will only run the main function if it is not called ny Dot Source, as a pester script would. I am using this instead of splitting functions to another file where it is not needed. Then pester runs without invoking the Main function.

if (-not ($MyInvocation.InvocationName -eq "."))
{
    Main
}

Upvotes: 0

Mark Wragg
Mark Wragg

Reputation: 23395

Per the comments, the issue is that your script is self-executing, so it's not possible to load it for Pester via dot-sourcing without also executing the functions.

The workaround suggested by Roman to temporarily use Set-Alias to set main to Out-Null would work. Another suggestion would be to separate your functions to a separate file (e.g named functions.ps1) and then in your original script you can use dot sourcing to include it, but in your test script just dot source the functions.ps1 file.

Upvotes: 5

Related Questions