PowerShell How-To

How To Run Code Before and After a Pester Test

If you're writing PowerShell, you're also writing tests for that code, right? Let's just assume we live in a perfect world and everyone does for a moment. If not, I highly encourage you to check out Pester. Since you are writing tests for PowerShell scripts and modules, it's likely you've run into the problem of needing to do some kind of "setup" and "teardown" tasks before and after your tests run. Rather than just throw some code in before your test and after, Pester provides a native ability to invoke this code.

Why would running code before and after each test even be a necessity anyway? Because there are times when in order to perform a specific test, the state of the test must be in a specific state or something invoked inside of the test itself must be at some predefined point. For example, when testing code that uses PowerShell remoting, it's common to need a PS remoting session active before the test is run to test with. Likewise, it's not good practice to leave it around after the test ends so it needs to be cleaned up. Regardless of the case, Pester allows this behavior.

There are two ways that Pester allows invoking code before and after tests. Pester can execute code before and after each test or before and after every test. The code to do so is similar but will behave much differently.

Let's start with an example. Perhaps I've got a single describe block that contains two tests (it blocks) for a function called Do-ThingToServer. This function has a parameter called Session that represents a connected PowerShell remoting session. Once passed, the Do-ThingToServer function invokes some kind of code on the server.

The function and tests looks like below:

function Do-ThingToServer  {
param(
$Session
)
Invoke-Command -Session $Session -ScriptBlock { 'hello' }
}

describe 'Do-Thing' {
it 'does that thing when the session for FOO is passed to it' {
Do-ThingToServer -Session $Session
}

    'it does not do that thing when the session for FOO is passed to it' {
Do-ThingToServer -Session $Session
}
}

When creating unit tests for this function, it's important not to actually attempt to open a PowerShell remoting session for this computer. Instead, we need to create a "fake" session to pass to the function. Inside of each it block where does the $Session variable come from? It can come from using Pester's BeforeEach and AfterEach constructs. Perhaps I'd like to create a new session before each test runs. My test would then turn into something that looks like this:

describe 'Do-Thing' {

    BeforeEach {
$script:session = New-PSSession -ComputerName localhost
}

    AfterEach {
$script:session | Remove-PSSession
}

    it 'does that thing when the session for FOO is passed to it' {
Do-ThingToServer -Session $script:Session
}

    'it does not do that thing when the session for FOO is passed to it' {
Do-ThingToServer -Session $script:Session
}
}

Notice in this case, since I'm using the result of New-PSSession in my tests, I need to set the scope to script so that the tests can see that variable. This is unrelated to the actual functionality of BeforeEach and AfterEach.

In the configuration, above, a new session will be created before each it block is invoked and it will be removed afterwards. Similarly, since I'm not actually changing the session between tests I could choose to just create a new session once and then tear it down once. This would prevent one extra session from being created. To ensure code is executed before and after all tests are complete we can use the BeforeAll and AfterAll constructs.

BeforeAll {
$script:session = New-PSSession -ComputerName localhost
}

AfterAll {
$script:session | Remove-PSSession
}

Now we can be sure that only a single remoting session would be created right before the first test and immediately after the last test.

Next time, rather than inserting code directly into the describe block, consider using each of the constructs we've went over today. You'll find that it makes executing code before and after your tests much easier to maintain.

About the Author

Adam Bertram is a 20-year veteran of IT. He's an automation engineer, blogger, consultant, freelance writer, Pluralsight course author and content marketing advisor to multiple technology companies. Adam also founded the popular TechSnips e-learning platform. He mainly focuses on DevOps, system management and automation technologies, as well as various cloud platforms mostly in the Microsoft space. He is a Microsoft Cloud and Datacenter Management MVP who absorbs knowledge from the IT field and explains it in an easy-to-understand fashion. Catch up on Adam's articles at adamtheautomator.com, connect on LinkedIn or follow him on Twitter at @adbertram or the TechSnips Twitter account @techsnips_io.


comments powered by Disqus
Most   Popular