Skip to main content

Setup and Teardown

Continuing down the path of writing more complex tests with Pineapple, there may be certain situations where you need to perform some additional setup & teardown. While not strictly required, these are recommended to be embedded in a .test.js file.

Global Before / After Hooks

Before All / After All

If you wish to run a function before / after all of the tests are run (like an initial setup for every single test you're going to run), you are able to simply add a JSDoc annotation to a function without any other descriptors.

/**
* For the purposes of demonstrating beforeAll, this function
* is an async function that populates either some set or a database
* @beforeAll
*/
export async function initializeCityDatabase() {
// ...
}


/**
* @test 'Vienna' resolves truthy
* @test 'San Juan' resolves truthy
* @test 'United Kingdom' resolves falsy
*/
export async function isCity(city) {
...
}

The annotations for this are beforeAll and afterAll. You may define multiple functions to be invoked.

Before / After (Global)

If you need to run a function before or after each set of tests (before each function), you may use the @beforeGlobal and @afterGlobal tags.

/**
* @beforeGlobal
*/
function resetCounts() {
// ...
}

This might make sense if you're using a framework and you're performing measurements in each test set.

Before Each / After Each (Global)

If you need to run a function before or after each test (every individual test case), you may use the @beforeEachGlobal and @afterEachGlobal tags.

/**
* @beforeEachGlobal
*/
function resetCounts() {
// ...
}

This might make sense if you're using a framework and you're performing measurements in each test.

Before / After tests within a function

If you need to run the setup with the function itself, you have multiple options; these annotation require you to specify what you'd like to have executed (usually methods imported using pineapple_import).

Before / After

These annotations will run before / after all of the test cases associated with the function.


/**
* @pineapple_import
*/
export async function setupExample (name) {
await db.initialize()
await db.insert({
name
})
}

/**
* @pineapple_import
*/
export async function destroyExample (name) {
await db.remove({ name })
}

/**
* Silly example, but shows how to invoke.
* @before setupExample('Jesse')
* @after destroyExample('Jesse')
* @test 'Jesse' returns 1
* @test 'John' returns 0
*/
export async function example(name) {
const people = await db.find({ name })
return people.length
}

BeforeEach / AfterEach

If you'd prefer for it to run before / after each test case,

let person

/**
* Sets a file-scoped variable to be a new person object.
* @pineapple_import
*/
export function createPerson (name, level) {
person = {
name,
level
}
}

/**
* Another silly example to demonstrate the use of beforeEach
* @beforeEach createPerson('John', 5)
* @test 3 returns @.level === 8
* @test 1 returns @.level === 6
*/
export function levelUp(amount) {
person.level += amount
return person
}

Caveat Emptor

In general, I'd encourage you to avoid using these annotations. They have been added to the framework to introduce flexibility, but if your tests necessitate the use of setup & teardown, you likely have a use-case that unit tests aren't ideal for. Scenario Tests with a framework like Cucumber would likely be far more fitting.

There might be some use-cases where @beforeAll and @afterAll might make sense, but please be mindful that there may be better options.