k6 Test Maintenance
Maintain, fix, and improve existing k6 test scripts. Five maintenance tasks,
each with a step-by-step procedure in references/workflows.md:
- Threshold tightening -- adjust threshold values based on observed metrics
- Version migration -- update scripts for new k6 releases
- Service change adaptation -- fix tests when the underlying service changes
- Refactoring -- clean up and modernize test code
- Best practices audit -- check scripts against current k6 best practices
Core principle: behavior-aware change control
Classify every proposed change by whether it alters the test's runtime behavior:
- Syntactic (behavior unchanged): the k6 runtime produces identical metrics,
pass/fail results, and endpoints. Examples: rename a variable,
let → const,
remove unused imports, update comments, reformat. Apply directly.
- Behavioral (behavior differs): anything affecting metrics, pass/fail,
timing, request targets, or load shape. Examples: threshold value changes,
adding
sleep(), endpoint URL updates, check rewrites, scenario changes, new
thresholds. Always present as a diff with rationale and require confirmation.
The threshold for "behavioral" is deliberately low. If in doubt, treat it as
behavioral and ask -- a trivial-looking threshold change can cascade to CI
gates, SLO calculations, and alerting.
Dependencies
k6-manage -- fetch and edit GCk6-hosted scripts safely (§5: GET, backup,
edit, validate, PUT, verify by sha256). Read it before touching any
cloud-hosted script.
gcx -- sole tool for Grafana Cloud API access.
- mcp-k6 tools --
validate_script and get_documentation. Check
availability first; fall back to k6 x docs if absent.
k6 x docs CLI -- documentation lookup when mcp-k6 isn't configured.
k6 CLI -- local validation (k6 inspect, k6 run).
Validation loop (every edit)
Every workflow produces a modified script. Never present or PUT an unvalidated
script -- run this loop, fixing and re-running until it passes:
- Parse-check:
k6 inspect <script> -- catches syntax errors, invalid
options, broken imports. Works on all types including browser tests (no
browser needed). If mcp-k6 is available, also run validate_script.
- Local smoke (non-browser, service reachable):
k6 run --vus 1 --iterations 1 <script>.
- Classify the change (below) and verify per the matrix -- recipes in
references/verification.md.
- Cloud-hosted scripts: apply via the k6-manage §5 safe-edit recipe
(GET → backup → edit → validate → PUT as
application/octet-stream →
sha256-verify).
Change classification
- Class A -- declarative-config only. The diff is confined to
options.thresholds or similar declarative fields that don't alter what the
k6 runtime executes; the bytes inside default function, imported modules,
and check predicates are byte-identical. Example: p(95)<500 → p(95)<420.
- Class B -- runtime logic changes. Any change to
default function,
imports, helper modules, request URLs, check predicates, or to
scenarios.*.vus/iterations/duration/executor (which alter load shape
and metric distributions). Example: changing a URL, adding a check, rewriting
auth, switching executors.
When in doubt, treat as Class B.
Verification matrix
| Class |
Test duration |
Verification |
| A |
any |
sha256 + k6 inspect + historical pass/fail prediction. No cloud run needed. |
| B |
short (< 5 min) |
sha256 + k6 inspect + full cloud run (k6-manage §11). |
| B |
long (≥ 5 min) |
sha256 + k6 inspect + local 1-iteration smoke + k6 cloud run of a local copy with --vus 1 --iterations 1. PUT to the saved test only after the cloud smoke passes. |
Verification depth depends on the change class, not the test's duration -- most
edits don't need a full run, and production tests may run for hours. Per-class
recipes (Class A prediction table, Class B short/long, edge cases like scenario
changes and loosening) are in
references/verification.md.
Documentation lookup
Before proposing any change that touches k6 APIs, imports, or patterns, confirm
it against current docs and cite the source in your report -- this grounds
recommendations in the real API, not stale model knowledge. Look up in order:
- mcp-k6 (preferred):
get_documentation("best_practices"),
get_documentation("javascript-api/k6-browser"), validate_script(...).
k6 x docs CLI (always available):k6 x docs using-k6 thresholds
k6 x docs javascript-api k6-http
k6 x docs search "websocket migration"
2-call strategy: try the direct path first; if it returns a topic list, pick
the subtopic and call again. Full parent paths required (using-k6 thresholds,
not thresholds). k6 x docs serves docs for the installed k6 version -- it
may lag the target version when migrating.
- Web fetch (last resort):
https://grafana.com/docs/k6/latest/.
Async check pattern
A common browser-test bug: using check() from k6 with async predicates. The
built-in check() does not await Promises, so
check(page, { 'title': p => p.locator('h1').textContent() === 'Foo' }) silently
passes because the Promise object is truthy. Two valid fixes:
- Async-aware check from jslib:
import { check } from 'https://jslib.k6.io/k6-utils/1.5.0/index.js' -- then
predicates can be async and await inside them works.
- Resolve the value before the check:
const text = await page.locator('h1').textContent(); check(text, { ... }) --
keeps the standard sync check from k6.
When you hit this during any workflow (migration, refactor, audit), flag it as a
behavioral bug and propose one of these fixes.
Script sources
- GCk6-hosted -- fetched and pushed via
k6-manage §5 (GET → backup → edit →
validate → PUT → verify sha256).
- Local on disk -- read and edit directly. Validate before presenting.
Determine the source before starting: a GCk6 test URL or ID is cloud-hosted; a
file path is local.
Workflows
Full procedures are in references/workflows.md:
- Threshold tightening --
propose values with observed-metric justification, diff, apply, Class A verify.
- Version migration -- find
deprecated/renamed APIs, classify syntactic vs behavioral, apply, Class B verify.
- Service change adaptation
-- map each service change to a script change, propose fixes, Class B verify.
- Refactoring -- find issues,
auto-apply syntactic, propose behavioral, Class B verify after confirmation.
- Best practices audit --
doc-driven audit across thresholds, load design, resource management, code
quality, and browser specifics.
All five follow behavior-aware change control: auto-apply syntactic changes,
present behavioral ones as diffs for confirmation.
Gotchas
| Issue |
Detail |
| Cloud script format |
GCk6 scripts can be single files or tar archives. Detect with file(1) before editing (see k6-manage §5). |
| Zero-observation thresholds |
A threshold on a metric with no observations passes by default. When adding new thresholds, ensure the metric is actually emitted by the test. |
| abortOnFail cascades |
If a threshold has abortOnFail: true, tightening it means runs abort earlier. Warn the user. |
| Browser script validation |
Browser scripts can't be validated with k6 run --iterations 1 without a browser. Use k6 inspect for parse-only validation, or validate_script via mcp-k6. |
| k6 x docs version alignment |
k6 x docs serves docs for the installed k6 version; when migrating to a newer version, local docs may not reflect the target API. Note this in migration lookups. |
| Script drift after edit |
After pushing a cloud-hosted script, the next run uses the new version, but historical runs keep their bundled snapshot. To investigate a past failure, compare the run-bundled script (read-only), not the current one. |
References
references/workflows.md -- step-by-step procedures for the five maintenance tasks
references/verification.md -- per-class post-edit verification recipes
1---
2name: k6-test-maintenance
3description: >
4 Maintain and improve existing k6 test scripts. Covers threshold tightening
5 based on trend data, version migration between k6 releases, auto-fixing tests
6 when the underlying service changes, refactoring for cleanliness, and auditing
7 scripts against current best practices from docs. Use when the user asks to
8 fix a failing k6 test, tighten thresholds, migrate a script to a new k6
9 version, refactor a test, update a script after a service change, or improve
10 a script with best practices. Trigger on phrases like "fix my k6 test",
11 "tighten my thresholds", "migrate to k6 v2", "update my test script",
12 "refactor this k6 test", "my test is failing after a deploy", "apply best
13 practices to my script", "modernize my k6 test", or "the service changed and
14 my test broke". Also trigger when another skill (k6-trend-analysis or
15 k6-cloud-investigate-test) hands off with a recommendation to edit a script.
16---
17
18# k6 Test Maintenance
19
20Maintain, fix, and improve existing k6 test scripts. Five maintenance tasks,
21each with a step-by-step procedure in [`references/workflows.md`](references/workflows.md):
22
231. **Threshold tightening** -- adjust threshold values based on observed metrics
242. **Version migration** -- update scripts for new k6 releases
253. **Service change adaptation** -- fix tests when the underlying service changes
264. **Refactoring** -- clean up and modernize test code
275. **Best practices audit** -- check scripts against current k6 best practices
28
29## Core principle: behavior-aware change control
30
31Classify every proposed change by whether it alters the test's runtime behavior:
32
33- **Syntactic** (behavior unchanged): the k6 runtime produces identical metrics,
34 pass/fail results, and endpoints. Examples: rename a variable, `let` → `const`,
35 remove unused imports, update comments, reformat. **Apply directly.**
36- **Behavioral** (behavior differs): anything affecting metrics, pass/fail,
37 timing, request targets, or load shape. Examples: threshold value changes,
38 adding `sleep()`, endpoint URL updates, check rewrites, scenario changes, new
39 thresholds. **Always present as a diff with rationale and require confirmation.**
40
41The threshold for "behavioral" is deliberately low. If in doubt, treat it as
42behavioral and ask -- a trivial-looking threshold change can cascade to CI
43gates, SLO calculations, and alerting.
44
45## Dependencies
46
47- **`k6-manage`** -- fetch and edit GCk6-hosted scripts safely (§5: GET, backup,
48 edit, validate, PUT, verify by sha256). Read it before touching any
49 cloud-hosted script.
50- **`gcx`** -- sole tool for Grafana Cloud API access.
51- **mcp-k6** tools -- `validate_script` and `get_documentation`. Check
52 availability first; fall back to `k6 x docs` if absent.
53- **`k6 x docs`** CLI -- documentation lookup when mcp-k6 isn't configured.
54- **`k6` CLI** -- local validation (`k6 inspect`, `k6 run`).
55
56## Validation loop (every edit)
57
58Every workflow produces a modified script. Never present or PUT an unvalidated
59script -- run this loop, fixing and re-running until it passes:
60
611. **Parse-check**: `k6 inspect <script>` -- catches syntax errors, invalid
62 options, broken imports. Works on all types including browser tests (no
63 browser needed). If mcp-k6 is available, also run `validate_script`.
642. **Local smoke** (non-browser, service reachable):
65 `k6 run --vus 1 --iterations 1 <script>`.
663. **Classify the change** (below) and **verify per the matrix** -- recipes in
67 [`references/verification.md`](references/verification.md).
684. **Cloud-hosted scripts**: apply via the k6-manage §5 safe-edit recipe
69 (GET → backup → edit → validate → PUT as `application/octet-stream` →
70 sha256-verify).
71
72### Change classification
73
74- **Class A -- declarative-config only.** The diff is confined to
75 `options.thresholds` or similar declarative fields that don't alter what the
76 k6 runtime executes; the bytes inside `default function`, imported modules,
77 and check predicates are byte-identical. Example: `p(95)<500` → `p(95)<420`.
78- **Class B -- runtime logic changes.** Any change to `default function`,
79 imports, helper modules, request URLs, check predicates, or to
80 `scenarios.*.vus`/`iterations`/`duration`/`executor` (which alter load shape
81 and metric distributions). Example: changing a URL, adding a check, rewriting
82 auth, switching executors.
83
84When in doubt, treat as Class B.
85
86### Verification matrix
87
88| Class | Test duration | Verification |
89|-------|---------------|--------------|
90| **A** | any | sha256 + `k6 inspect` + **historical pass/fail prediction**. No cloud run needed. |
91| **B** | short (< 5 min) | sha256 + `k6 inspect` + **full cloud run** (k6-manage §11). |
92| **B** | long (≥ 5 min) | sha256 + `k6 inspect` + **local 1-iteration smoke** + **`k6 cloud run` of a local copy with `--vus 1 --iterations 1`**. PUT to the saved test only after the cloud smoke passes. |
93
94Verification depth depends on the change class, not the test's duration -- most
95edits don't need a full run, and production tests may run for hours. Per-class
96recipes (Class A prediction table, Class B short/long, edge cases like scenario
97changes and loosening) are in
98[`references/verification.md`](references/verification.md).
99
100## Documentation lookup
101
102Before proposing any change that touches k6 APIs, imports, or patterns, confirm
103it against current docs and **cite the source** in your report -- this grounds
104recommendations in the real API, not stale model knowledge. Look up in order:
105
1061. **mcp-k6** (preferred): `get_documentation("best_practices")`,
107 `get_documentation("javascript-api/k6-browser")`, `validate_script(...)`.
1082. **`k6 x docs`** CLI (always available):
109 ```bash
110 k6 x docs using-k6 thresholds
111 k6 x docs javascript-api k6-http
112 k6 x docs search "websocket migration"
113 ```
114 2-call strategy: try the direct path first; if it returns a topic list, pick
115 the subtopic and call again. Full parent paths required (`using-k6 thresholds`,
116 not `thresholds`). `k6 x docs` serves docs for the installed k6 version -- it
117 may lag the target version when migrating.
1183. **Web fetch** (last resort): `https://grafana.com/docs/k6/latest/`.
119
120## Async check pattern
121
122A common browser-test bug: using `check()` from `k6` with async predicates. The
123built-in `check()` does not await Promises, so
124`check(page, { 'title': p => p.locator('h1').textContent() === 'Foo' })` silently
125passes because the Promise object is truthy. Two valid fixes:
126
127- **Async-aware check from jslib**:
128 `import { check } from 'https://jslib.k6.io/k6-utils/1.5.0/index.js'` -- then
129 predicates can be `async` and `await` inside them works.
130- **Resolve the value before the check**:
131 `const text = await page.locator('h1').textContent(); check(text, { ... })` --
132 keeps the standard sync `check` from `k6`.
133
134When you hit this during any workflow (migration, refactor, audit), flag it as a
135behavioral bug and propose one of these fixes.
136
137## Script sources
138
139- **GCk6-hosted** -- fetched and pushed via `k6-manage` §5 (GET → backup → edit →
140 validate → PUT → verify sha256).
141- **Local on disk** -- read and edit directly. Validate before presenting.
142
143Determine the source before starting: a GCk6 test URL or ID is cloud-hosted; a
144file path is local.
145
146## Workflows
147
148Full procedures are in [`references/workflows.md`](references/workflows.md):
149
150- **[Threshold tightening](references/workflows.md#threshold-tightening)** --
151 propose values with observed-metric justification, diff, apply, Class A verify.
152- **[Version migration](references/workflows.md#version-migration)** -- find
153 deprecated/renamed APIs, classify syntactic vs behavioral, apply, Class B verify.
154- **[Service change adaptation](references/workflows.md#service-change-adaptation)**
155 -- map each service change to a script change, propose fixes, Class B verify.
156- **[Refactoring](references/workflows.md#refactoring)** -- find issues,
157 auto-apply syntactic, propose behavioral, Class B verify after confirmation.
158- **[Best practices audit](references/workflows.md#best-practices-audit)** --
159 doc-driven audit across thresholds, load design, resource management, code
160 quality, and browser specifics.
161
162All five follow behavior-aware change control: auto-apply syntactic changes,
163present behavioral ones as diffs for confirmation.
164
165## Gotchas
166
167| Issue | Detail |
168|-------|--------|
169| **Cloud script format** | GCk6 scripts can be single files or tar archives. Detect with `file(1)` before editing (see k6-manage §5). |
170| **Zero-observation thresholds** | A threshold on a metric with no observations passes by default. When adding new thresholds, ensure the metric is actually emitted by the test. |
171| **abortOnFail cascades** | If a threshold has `abortOnFail: true`, tightening it means runs abort earlier. Warn the user. |
172| **Browser script validation** | Browser scripts can't be validated with `k6 run --iterations 1` without a browser. Use `k6 inspect` for parse-only validation, or `validate_script` via mcp-k6. |
173| **k6 x docs version alignment** | `k6 x docs` serves docs for the installed k6 version; when migrating to a newer version, local docs may not reflect the target API. Note this in migration lookups. |
174| **Script drift after edit** | After pushing a cloud-hosted script, the next run uses the new version, but historical runs keep their bundled snapshot. To investigate a past failure, compare the run-bundled script (read-only), not the current one. |
175
176## References
177
178- [`references/workflows.md`](references/workflows.md) -- step-by-step procedures for the five maintenance tasks
179- [`references/verification.md`](references/verification.md) -- per-class post-edit verification recipes