Mocking your functions in Powershell unit tests

Written by Troy on September 13, 2012 Categories: Uncategorized Tags: , , , , , ,

I’m currently using PSUnit to unit test my Powershell scripts. It can be tricky unit testing a ‘glue’ language, since you may be calling many legacy scripts and console applications. However the beauty of Powershell’s syntactic style is that it leads you to cloak underlying operations with meaningful ‘verb-noun’ function names. For example:

function Install-MyThing
{
   if ( !(Test-MyThing) )
   {
      Invoke-Expression -Command "\\foo\bar\thing.exe install /wibble"
   }
}

function Test-MyThing
{
   $output = \\foo\bar\thing.exe validate
   return ($output -ne $null) -and ($output -like "*things are good*")
}

Which then allows us to think about mocking. My first forays led me to use aliases to mock calls, which is entirely useful. However I came up with a simple mechanism to allow me to mock functions:

function script:Backup-Function($name)
{
    $tempName = "function:script:pre-mock-$name"
    if ( !(Test-Path $tempName) )
    {
        copy function:\$name $tempName
    }
}

function script:New-MockFunction($name, $scriptBlock)
{
    Backup-Function $name
    Set-Item -Path function:\$name -Value $scriptBlock
}

function script:Remove-MockFunctions
{
    dir function:pre-mock-* | % { Restore-Function $_.Name.substring(9) }    
}

function script:Restore-Function($name)
{
    copy function:\pre-mock-$name function:script:$name
    del function:\pre-mock-$name
}
...

function Get-FooBar
{
    if (Test-Wibble)
    {
         return "baz"
    }
    else { ... }
}

function Test-Wibble
{
    # something terribly complex...
}

function Test.Get-FooBar_ReturnsBaz_IfWibble
{
    SetUp
    New-MockFunction -Name Test-Wibble -ScriptBlock { return $true }

    $foo = Get-FooBar

    Assert-That -ActualValue $foo -Constraint { $actualValue -eq "baz" }
    Remove-MockFunctions # or put in SetUp or TearDown etc
}

The Powershell function: provider is a tricky thing!

  • Backup-Function on line 1 simply duplicates the function with a new name, if it does not already exist.
  • New-MockFunction on line 10 will back up our function before replacing the definition with our mocked script block.
  • Restore-Function on line 21 copies what we backed up over the redefined (mock) function body, and is called for all backed up functions by Remove-MockFunctions

Take note of your scopes here, this sample only applies to script: scope, but that seemed appropriate given I was unit testing a Powershell script file.

No Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>