CSV parameterization in k6 allows you to drive tests with dynamic data, simulating real-world scenarios where user behavior and input vary.

Let’s see k6 in action with CSV data. Imagine you’re testing an API endpoint that requires a unique user_id and api_key for each request. We’ll simulate this by reading these values from a CSV file.

First, create a simple CSV file named data.csv:

user_id,api_key
user1,keyabc123
user2,keydef456
user3,keyghi789
user4,keyjkl012

Now, here’s the k6 script (script.js) that uses this data:

import http from 'k6/http';
import { SharedArray } from 'k6/data';

// Load data from CSV file
const data = new SharedArray('user credentials', function() {
    return JSON.parse(open('./data.csv'));
});

export const options = {
    vus: 4, // Number of virtual users
    duration: '30s', // Test duration
};

export default function () {
    // Pick a random row from the data array for each VU iteration
    const row = data[__VU % data.length];
    const userId = row.user_id;
    const apiKey = row.api_key;

    const url = `https://api.example.com/data/${userId}`;
    const params = {
        headers: {
            'X-API-Key': apiKey,
        },
    };

    http.get(url, params);

    // Optional: Add a sleep to simulate user think time
    // sleep(1);
}

To run this, make sure data.csv and script.js are in the same directory. Then, execute:

k6 run script.js

In this script:

  • import { SharedArray } from 'k6/data';: This imports the SharedArray construct, which is crucial for efficiently loading and sharing data across virtual users.
  • const data = new SharedArray('user credentials', function() { return JSON.parse(open('./data.csv')); });: This is where the magic happens. SharedArray ensures that the data.csv file is read only once by the k6 process, and then its content is made available to all virtual users. We use open('./data.csv') to read the file content and JSON.parse() because k6’s open() function reads files as raw strings, and we’ve structured our CSV to be easily parseable as JSON-like objects (each row is an object with keys matching the header).
  • export const options = { ... };: Defines the test configuration. Here, vus: 4 means we’ll have 4 virtual users running concurrently.
  • export default function () { ... }: This is the main function executed by each virtual user.
  • const row = data[__VU % data.length];: This line selects a data row. __VU is a special k6 variable representing the current virtual user ID (starting from 0). The modulo operator (%) ensures that we cycle through the available data rows. If you have 1000 users and 100 data rows, each user will get a data row, and then the cycle repeats. This is how you achieve "thousands of users" with a manageable dataset.
  • const userId = row.user_id; and const apiKey = row.api_key;: These lines extract the specific values for the current iteration.
  • http.get(url, params);: This makes the actual HTTP request to your target API, using the dynamically loaded userId and apiKey.

The most surprising thing about SharedArray is that it’s not just for reading files. It’s a general-purpose mechanism for sharing any JavaScript object or array across all VUs. You can pre-calculate complex data structures, generate lookup tables, or even load configurations from multiple sources into a single SharedArray before your test begins, all without the overhead of each VU independently reading or generating the same data. This is fundamental to achieving high performance in k6 when dealing with external data.

This setup allows you to simulate a large number of users, each with unique credentials, without having to manually create an enormous CSV. The key is SharedArray’s efficient data sharing and the modulo operator for cycling through your data.

The next concept you’ll likely explore is how to handle more complex data relationships, such as ensuring that each virtual user consistently uses the same data row throughout their lifecycle, rather than cycling.

Want structured learning?

Take the full K6 course →