Understanding the four distinct lifecycle stages in a k6 test: init, setup, VU execution, and teardown.
Every k6 test executes through distinct lifecycle stages in a specific order. Understanding this lifecycle helps you structure your tests correctly and avoid common mistakes.
The init stage is required and runs before the test begins. Code in the init context executes once per VU to initialize the test conditions.All code outside of lifecycle functions is init code and executes first.
The VU stage is required and contains the actual test workload. Scripts must define at least one scenario function (typically default) that runs repeatedly.
Setup runs once at the beginning of the test, after init but before VU execution:
import http from 'k6/http';export function setup() { // Runs once to prepare the test const res = http.get('https://quickpizza.grafana.com/api/json'); return { data: res.json() };}export default function (data) { // Can access data returned from setup console.log(JSON.stringify(data));}
Unlike init code, setup functions can make HTTP requests since they run during test execution.
Teardown runs once at the end of the test, after all VU execution completes:
export function teardown(data) { // Runs once to clean up console.log('Test completed with data:', JSON.stringify(data)); // Could send a webhook notification // http.post('https://example.com/webhook', JSON.stringify(data));}
If the setup() function throws an error, teardown() will not run. Add error handling to setup to ensure proper cleanup.
The setup() function can return data that gets passed to both default() and teardown():
export function setup() { return { username: 'testuser', timestamp: new Date().toISOString() };}export default function (data) { // Each VU gets a copy of the data console.log(`User: ${data.username}`);}export function teardown(data) { // Teardown also receives the data console.log(`Test started at ${data.timestamp}`);}