Version: v5

Setup and teardown

Setup and teardown

Pester offers multiple ways to run code before, and after your tests to set them up, and clean up after them. The setup is represented by a BeforeAll, and BeforeEach blocks.

The teardown is used to clean up after test or a block and is guaranteed to run even if the test fails. Teardown is represented by AfterAll and AfterEach blocks.

BeforeAll

BeforeAll is used to share setup among all the tests in a Describe / Context including all child blocks and tests. BeforeAll runs during Run phase and runs only once in the current block.

The typical usage is to setup the whole test script, most commonly to import the tested function, by dot-sourcing the script file that contains it:

BeforeAll {
. $PSCommandPath.Replace('.Tests.ps1','.ps1')
}
Describe "API validation" {
# ...
}

Another typical usage is to do an expensive operation once, and then validate the result in multiple tests:

Describe "API validation" {
BeforeAll {
# this calls REST API and takes roughly 1 second
$response = Get-Pokemon -Name Pikachu
}
It "response has Name = 'Pikachu'" {
$response.Name | Should -Be 'Pikachu'
}
It "response has Type = 'electric'" {
$response.Type | Should -Be 'electric'
}
}

BeforeEach

BeforeEach runs once before every test in the current or any child blocks. Typically this is used to create all the prerequisites for the current test, such as writing content to a file. For example this is how we ensure that each test gets fresh file:

Describe "File parsing" {
BeforeEach {
# randomized path, to get fresh file for each test
$path = "$([IO.Path]::GetTempPath())/$([Guid]::NewGuid())_form.xml"
Copy-Item -Source $template -Destination $path -Force | Out-Null
}
It "Writes username" {
Write-XmlForm -Path $file -Field "username" -Value "nohwnd"
$content = Get-Content $file
# ...
}
It "Writes name" {
Write-XmlForm -Path $file -Field "name" -Value "Jakub"
$content = Get-Content $file
# ...
}
}

AfterEach

AfterEach runs once after every test in the current or any child blocks. Typically this is used to clean up resources created by the test, or its setups. AfterEach runs in a finally block, and is guaranteed to run even if the test (or setup) fails. For example this is how we ensure that each test removes its test file:

Describe "File parsing" {
BeforeEach {
# randomized path, to get fresh file for each test
$path = "$([IO.Path]::GetTempPath())/$([Guid]::NewGuid())_form.xml"
Copy-Item -Source $template -Destination $path -Force | Out-Null
}
It "Writes username" {
Write-XmlForm -Path $file -Field "username" -Value "nohwnd"
$content = Get-Content $file
# ...
}
It "Writes name" {
Write-XmlForm -Path $file -Field "name" -Value "Jakub"
$content = Get-Content $file
# ...
}
AfterEach {
if (Test-Path $file) {
Remove-Item $file -Force
}
}
}

AfterEach placement within the current block does not affect when it is run, you can place it on the top or on the bottom of the block, and it will still run last.

The teardown in AfterEach should be prepared to run at any place of BeforeEach or It. For example when the $file does not exist yet, because we got AccessDenied when trying to write it.

AfterAll

AfterAll runs once after every Describe or Context block. It is used to clean up common resources. It works the same as AfterEach, except that it just runs once.

Multiple setups and teardowns

When multiple BeforeAll are defined, they run in the order in which they were defined. AfterAll run in the opposite order.

When multiple BeforeEach are defined, they run in the order in which they were defined, and they run right before the test they setup. AfterEach run right after the test is finished, in the opposite order.

There can be only one setup or teardown of each kind in a block.

BeforeAll {
Write-Host "-> Top-level BeforeAll"
}
Describe "d" {
BeforeAll {
Write-Host "-> Describe BeforeAll"
}
BeforeEach {
Write-Host "-> Describe BeforeEach"
}
Context "Whitespace" {
BeforeAll {
Write-Host "-> Context BeforeAll"
}
BeforeEach {
Write-Host "-> Context BeforeEach"
}
It "i" {
# ...
}
AfterEach {
Write-Host "-> Context AfterEach"
}
AfterAll {
Write-Host "-> Context AfterAll"
}
}
AfterEach {
Write-Host "-> Describe AfterEach"
}
AfterAll {
Write-Host "-> Describe AfterAll"
}
}
AfterAll {
Write-Host "-> Top-level AfterAll"
}
Running tests from 'xxx'
-> Top-level BeforeAll
-> Describe BeforeAll
-> Context BeforeAll
Describing d
Context Whitespace
-> Describe BeforeEach
-> Context BeforeEach
-> Context AfterEach
-> Describe AfterEach
[+] i 89ms (86ms|4ms)
-> Context AfterAll
-> Describe AfterAll
-> Top-level AfterAll
Tests completed in 543ms
Tests Passed: 1, Failed: 0, Skipped: 0 NotRun: 0

Skipping setups and teardowns

Setups and teardowns are skipped when the current tree won't result in any test being run. For example when running tests with a tag filter "Acceptance", all unit test files will have all their tests filtered out. The setups and teardowns in those files won't run at all.

Scoping

All variables defined in BeforeAll are available to all child blocks and tests. But because all child blocks and tests run in their own scopes, the variables are not writeable to them. This is needed to isolate tests from each other.

Describe "d" {
BeforeAll {
$a = "BeforeAll"
}
It "Write a" {
$a = "Test"
}
It "Check a" {
$a | Should -Be "BeforeAll"
}
AfterAll {
Write-Host "-> AfterAll"
}
}
Describing d
[+] Write a 7ms (1ms|6ms)
[+] Check a 4ms (3ms|1ms)
-> AfterAll
Tests completed in 347ms

BeforeEach, It and AfterEach run in the same scope. All variables in them are shared. This way you can move code between BeforeEach and It without changes. And you can also base your AfterEach on variables that were defined in It, for example to delete a file you created in it.

Describe "d" {
It "Write a" {
$a = "Test"
}
AfterEach {
Write-Host "-> $a"
}
}
Describing d
-> Test
[+] Write a 7ms (3ms|4ms)
Tests completed in 227ms