Getting Started With Pester

If you don’t know what Pester is, it’s a framework for running unit tests and validating PowerShell code. Also, it’s awesome. In May I finally dipped my toe in the water with a pretty simple test for a REALLY simple function. I’m not going to go into a world of detail on how exactly all my Pester code works because there are tons of guides for that. What I’m going to do instead is provide a quick run down of what I came up with.

First things first, I need a function to validate.

function Write-SomeMath 
{
    param(
        [int]$First,
        [int]$Second
    )
    return $First + $Second
}

I guess that will work. Write-SomeMath takes two integers and returns their sum. Hardly a breathtaking display of complexity and function but it will do just fine for this example.

Now I need to install Pester. The easiest way to do this is using the PSGet module in PowerShell 5.0 to get it from PowerShellGallery.com.

Install-Module Pester -Scope CurrentUser -Force
Import-Module Pester

The next thing I need is a Describe block.

Describe 'GoofingWithPester.ps1' {

}

This Describe block will contain and - you guessed it - describe the tests (I just used my filename) and provide a unique TestDrive (check out the getting started link).

Now I need a Context block.

Describe 'GoofingWithPester.ps1' {
    Context 'Write-SomeMath' {
        
    }
}

I’m further grouping my tests by creating a Context here for my Write-SomeMath function. This could have been named anything.

Now, I could start with a bunch of tests, but I want to show off a particular feature of Pester that allows you to pass an array of different test cases.

Describe 'GoofingWithPester.ps1' {
    Context 'Write-SomeMath' {
        $testcases = @(
            @{
                fir  = 1
                sec  = 2
                exp  = 3
                test = '1 and 2'
            }, 
            @{
                fir  = 3
                sec  = 6
                exp  = 91 #wrong on purpose
                test = '3 and 6 (wrong on purpose)'
            }, 
            @{
                fir  = 4
                sec  = 6
                exp  = 10
                test = '4 and 6'
            }
        )

    }
}

All I did was define an array called $testcases which holds an array of hash tables. It’s got the first number, second number, expected result and a name of what we’re testing. Now I can pass this entire array to a test rather than crafting different tests for all of them individually.

Describe 'GoofingWithPester.ps1' {
    Context 'Write-SomeMath' {
        $testcases = @(
            @{
                fir  = 1
                sec  = 2
                exp  = 3
                test = '1 and 2'
            }, 
            @{
                fir  = 3
                sec  = 6
                exp  = 91 #wrong on purpose
                test = '3 and 6 (wrong on purpose)'
            }, 
            @{
                fir  = 4
                sec  = 6
                exp  = 10
                test = '4 and 6'
            }
        )
        It 'Can add <test>' -TestCases $testcases {
            param($fir,$sec,$exp)
            Write-SomeMath -First $fir -Second $sec | Should Be $exp
        }

    }
}

This is an It block which is what Pester calls a test. I’ve named it “Can add <test>” and it will pull the “test” value from the hashtable and fill it in. Cool! I’m using the -TestCases parameter to pass my array of test cases to the It block. Then I’ve got parameters inside the test for my first value, second value and expected outcome. I execute Write-SomeMath with the values pulled from my test cases and pipe the result to “Should Be” to compare the outcome to my expected outcome.

Now, just one more test for fun. What if I don’t pass an integer to my function?

Describe 'GoofingWithPester.ps1' {
    Context 'Write-SomeMath' {
        $testcases = @(
            @{
                fir  = 1
                sec  = 2
                exp  = 3
                test = '1 and 2'
            }, 
            @{
                fir  = 3
                sec  = 6
                exp  = 91 #wrong on purpose
                test = '3 and 6 (wrong on purpose)'
            }, 
            @{
                fir  = 4
                sec  = 6
                exp  = 10
                test = '4 and 6'
            }
        )
        It 'Can add &lt;test&gt;' -TestCases $testcases {
            param($fir,$sec,$exp)
            Write-SomeMath -First $fir -Second $sec | Should Be $exp
        }
        It 'Detects wrong datatypes' {
            {Write-SomeMath -First 9 -Second 'cat'} | Should throw
        }
    }
}

Another It block for detecting wrong datatypes. I pipe the result into Should throw because my function should throw an error. For this to work properly, the code I’m testing has to be wrapped in a scriptblock, otherwise the thrown error will occur and be trapped in my function.

Here’s the outcome when I run this file!

[caption id=”attachment_355” align=”alignnone” width=”838”]Getting Started With Pester - Results Pester Results[/caption]

Pretty cool. My first test passes, the second one fails and tells me why, the third and fourth tests pass. The fourth one is especially interesting. The function FAILED but because the test said it SHOULD FAIL, the test itself passed.

So that’s my “dip my toes in the water” intro Pester test. Stay tuned for more complicated examples.

Written on June 22, 2016