skip to content

Which per-scenario keys let one k6 run drive several exported functions and keep their results separable?

level: middleimportance: should knowfreq 52%

answer

  1. three keys, three different jobs
  2. the default export is only a default
  3. checked before the run starts
  4. overlaid, not replaced

basics

~10 s

Three keys in the k6 scenario entry: exec names the exported function that scenario's VUs call, env overlays __ENV for that scenario only, and tags attaches key-value pairs to the samples the scenario emits.

solid answer

~40 s

`exec` is the key that breaks the one-script-one-function assumption: it names any function the script exports, and defaults to `"default"`. k6 validates it before the run, so a typo fails with `executor <scenario>: function '<name>' not found in exports` and exits 104 rather than failing halfway through. `env` is a string map that is overlaid on the process environment when a VU is activated for that scenario, so `__ENV.BASE_URL` can hold a different value in each scenario while the code reading it stays identical. `tags` is a string map applied over the run-level tags for that scenario's samples. Together they let one map hold a browse workload and a checkout workload that share a script but not a code path, an environment, or a results bucket.

code

javascript · 31 lines
javascript
import http from 'k6/http';

export const options = {
  scenarios: {
    browse: {
      executor: 'constant-vus',
      exec: 'browse',
      vus: 20,
      duration: '1m',
      env: { PATH_SUFFIX: '/' },
      tags: { journey: 'read' },
    },
    checkout: {
      executor: 'constant-vus',
      exec: 'checkout',
      vus: 3,
      duration: '1m',
      startTime: '10s',
      env: { PATH_SUFFIX: '/api/tools' },
      tags: { journey: 'write' },
    },
  },
};

export function browse() {
  http.get(`https://quickpizza.grafana.com${__ENV.PATH_SUFFIX}`);
}

export function checkout() {
  http.get(`https://quickpizza.grafana.com${__ENV.PATH_SUFFIX}`);
}

go deeper

for a junior

Know that exec picks which exported function a scenario runs and defaults to the default export, and that env and tags exist per scenario. Exporting the function is what makes it callable.

for a middle

Explain the overlay semantics: env is copied over the process environment per scenario, tags are applied over the run tags, and both are string-to-string maps with no coercion.

for a senior

Show that exec is validated against the script's exports at config time with exit 104, and design a map where each workload has its own code path, fixtures and tag rather than one branching function.

for a principal

Set the convention for a team: which dimensions belong in per-scenario tags versus the scenario name itself, given that both end up in dashboards and one of them is constrained to letters, digits, underscores and dashes.

## Why a scenario needs more than an executor An executor decides *how much* work a scenario does. The three keys below decide *what* the work is, *what it sees*, and *how you find it again in the results*. All three sit in the shared block of a scenario entry, so every one of k6's six executors accepts them, and all three have defaults that make them invisible until you need them. ## `exec` — which function the scenario runs By default a scenario's VUs call the script's default export, and that is exactly what `exec: "default"` means. Set `exec` to any other exported function name and that scenario's VUs call it instead: ```javascript export const options = { scenarios: { browse: { executor: 'constant-vus', exec: 'browse', vus: 20, duration: '1m' }, checkout: { executor: 'constant-vus', exec: 'checkout', vus: 3, duration: '1m', startTime: '10s' }, }, }; export function browse() { /* ... */ } export function checkout() { /* ... */ } ``` Two details matter in practice: - **It is validated at config time.** k6 collects the script's callable exports while building the bundle and checks every scenario's `exec` against them before the run starts. A name that is not exported produces `executor <scenario>: function '<name>' not found in exports`, a configuration error with exit code **104**. A misspelling therefore costs you seconds, not a whole run. - **Sharing is allowed.** Several scenarios may point at the same function; they remain separate scenarios with their own executors, offsets and tags. ## `env` — per-scenario environment variables `env` is an object of string keys to string values. When k6 activates a VU for a scenario, it builds that VU's `__ENV` by starting from the process environment and **overlaying the scenario's own map on top**. The consequences are worth stating precisely: - a key present in both wins for the scenario, so a scenario can override a global value for itself alone; - keys the scenario does not mention still come through from the environment; - the overlay is applied at activation, so it affects code running inside that scenario's iterations; - values are strings — `env: { RATE: 5 }` is a type error, not a number you can read back. This is how one script drives two targets or two data sets without a single `if` on the scenario name. ## `tags` — labelling what the scenario emits `tags` is likewise a map of strings, applied over the run's tags when the VU is activated for that scenario. Every sample that scenario emits carries them, which makes the results filterable by something you chose rather than only by scenario name. When the same VU is later handed to a different scenario, k6 deliberately clears the previous scenario's tags before applying the new ones, so tags never leak across scenarios. Note that k6 already adds a `scenario` tag with the scenario's name on its own; `tags` is for the dimension the name does not carry, such as `journey: 'write'` or `tier: 'anonymous'`. ## The three keys side by side | key | type | default | scope of its effect | |---|---|---|---| | `exec` | string | `"default"` | which exported function this scenario's VUs call | | `env` | object of strings | `{}` | the `__ENV` this scenario's code reads | | `tags` | object of strings | `{}` | the tags on the samples this scenario emits | ## Putting it together A realistic map uses all three at once: `exec` to give each workload its own code path, `env` to point that code path at its own fixtures, and `tags` to keep the two workloads apart in the output even when they overlap in time. The pattern to avoid is the opposite one — a single default function that branches on `__ENV` or on the scenario name — because it puts scheduling logic inside the iteration, where k6 cannot validate it and the summary cannot separate it. ## Mistakes worth naming 1. Setting `exec` to a function that exists in the file but is never exported; only exports are callable, and k6 rejects the rest at config time. 2. Expecting per-scenario `env` to reach code that does not run inside that scenario's iterations, since the overlay happens when a VU is activated for the scenario. 3. Writing non-string values in `env` or `tags` and expecting k6 to coerce them; both are string-to-string maps and a number fails to decode. 4. Assuming `tags` replaces the run's tags rather than being applied over them, and then wondering where a run-level tag went.

  • What does k6 do when a scenario's `exec` names a function the script does not export?
    It fails before running anything. k6 builds the list of callable exports from the script, then validates each scenario's `exec` against it, reporting `executor <scenario>: function '<name>' not found in exports` as invalid configuration and exiting 104. Catching it at config time is why a five-scenario script does not fail forty minutes in on a typo.
  • If a variable is set both in the process environment and in a k6 scenario's `env`, which value does the script see?
    The scenario's. k6 builds each VU's `__ENV` by copying the process environment first and then copying the scenario's `env` over it, so the scenario entry wins on any key it mentions and inherits every key it does not.

saying these in an interview costs you the question

  • Assuming every scenario must use the default exported function
  • Pointing exec at a function that is defined but not exported
  • Expecting numbers in env to arrive as numbers rather than strings
  • Thinking per-scenario tags replace the run-level tags
  • Branching on the scenario name inside one function instead of using exec