agentsclimarketplace

K6

Skill aks-builds/quality-skills/skills/k6

When the user wants to design, implement, debug, or operate k6 load tests. Use when the user mentions "k6," "Grafana k6," "k6 run," "k6 scenarios," "k6 thresholds," "vus," "iterations," "ramping-vus," "constant-arrival-rate," "k6 cloud," "xk6," "k6-operator," or "checks vs thresholds." For JMeter see jmeter. For Gatling see gatling. For Locust see locust. For Artillery see artillery. For overall perf testing strategy see test-strategy.From its SKILL.md

Install
npx -y skills add aks-builds/quality-skills --skill k6

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

9.7 KB, as published. Nobody here has run it

k6

You are an expert in Grafana k6 (formerly LoadImpact k6). Your goal is to help engineers write realistic load tests, set meaningful thresholds, choose the right scenario type, integrate k6 into CI, and avoid the most common load-testing anti-patterns. Don't fabricate k6 API methods, scenario executor names, or CLI flags. When uncertain, point the reader to grafana.com/docs/k6.

Initial Assessment

Check .agents/qa-context.md (fallback: .claude/qa-context.md) before answering. Pay attention to:

  • SLO targets — without a target (e.g., "p95 < 300ms at 200 RPS for /checkout"), load testing produces unactionable numbers.
  • Traffic profile — average vs peak vs spike vs soak. Different shapes need different scenario executors.
  • Target environment — production-like (perf env), staging, ephemeral. Never load-test prod without explicit owner consent.
  • Languages — k6 tests are JS modules. Teams comfortable with JS migrate fastest from existing tooling.
  • Existing perf tooling — migrating from JMeter changes how thinking-time, scenarios, and assertions look.

If the file does not exist, ask: SLO target (p50/p95/p99 + RPS), traffic profile, target environment, and whether results need to flow to Grafana / Datadog / k6 Cloud.


Why k6

  • JS scripting — readable, refactorable, version-controllable.
  • Goal-oriented thresholds — assertions on metrics that pass/fail the test.
  • Scenario executors — distinct shapes (constant load, ramping users, arrival-rate-driven) without writing them by hand.
  • Built-in metrics + tags — out-of-the-box latency, error rate, throughput; tag by URL / RPC for per-endpoint slicing.
  • Cloud + open source — same script runs locally and in k6 Cloud / k6-operator on Kubernetes.

When not to use k6:

  • JVM-deep teams that already have Gatling expertise → gatling.
  • Test scripts must be authored by non-coders → JMeter UI is often easier for that audience.
  • Massive distributed runs without managed cloud → k6-operator or k6 Cloud help; pure self-hosted distributed k6 is non-trivial.

Test anatomy

import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  scenarios: {
    smoke: {
      executor: 'constant-vus',
      vus: 5,
      duration: '30s',
    },
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],            // <1% errors
    http_req_duration: ['p(95)<300', 'p(99)<800'],
  },
};

export default function () {
  const res = http.get('https://staging.example.com/api/products', {
    tags: { name: 'GET /api/products' },
    headers: { Accept: 'application/json' },
  });
  check(res, {
    'status 200': r => r.status === 200,
    'has products': r => Array.isArray(r.json('products')),
  });
  sleep(1);
}
  • options declares scenarios and thresholds.
  • The default export function is one virtual-user iteration.
  • check records pass/fail without failing the test.
  • thresholds are the pass/fail gates of the run.

Scenarios (executors)

Pick the executor that matches the question you're answering:

ExecutorQuestion
constant-vus"What happens at a steady N concurrent users?"
ramping-vus"What happens as users ramp from 0 to N?"
constant-arrival-rate"What happens at a steady N requests per second?" — closer to real traffic
ramping-arrival-rate"What happens as RPS ramps up?"
per-vu-iterations"Each user performs this exact sequence N times"
shared-iterations"The pool collectively performs N iterations"
externally-controlled"Start/stop and adjust load via the API"

Arrival-rate executors are usually what you want for SLO-style testing. VU-based executors are sensitive to response time (slow responses = slow load); arrival-rate executors hold throughput regardless of latency.

Multiple scenarios can run in the same script; tag traffic per scenario for clean reporting.


Thresholds

Thresholds make the test pass/fail. Common patterns:

thresholds: {
  // Global
  http_req_duration: ['p(95)<300'],
  http_req_failed: ['rate<0.005'],

  // Per-endpoint (using tag)
  'http_req_duration{name:GET /api/products}': ['p(95)<200'],

  // Per-scenario
  'http_req_duration{scenario:checkout}': ['p(99)<1000'],

  // Custom metric
  'order_value_total': ['count>1000'],
}

Set thresholds matching your SLOs. A test without thresholds just produces numbers — pass/fail is what makes it a real CI gate.


Checks vs. thresholds

  • check(res, ...) records a passing/failing boolean per call, but does not fail the test.
  • threshold is the pass/fail gate.

Common mistake: writing many check()s and assuming a failed check fails the run. It doesn't (by default). Either:

  • Use thresholds on the check metric: 'checks{check:status 200}': ['rate>0.99'], or
  • Use fail() to abort, or
  • Run with --summary-mode full / --out experimental-prometheus-rw and let downstream dashboards alert.

Realistic data and stage setup

import { SharedArray } from 'k6/data';

const users = new SharedArray('users', () => JSON.parse(open('./users.json')));

export default function () {
  const u = users[Math.floor(Math.random() * users.length)];
  // ... use u.email
}

SharedArray loads test data once, shared across VUs.

For per-VU initialization (token, account setup), use the setup() function:

export function setup() {
  const token = http.post('https://staging.example.com/auth/login', {
    email: '[email protected]', password: 'Pa$$w0rd-fake',
  }).json('token');
  return { token };
}

export default function (data) {
  http.get('https://staging.example.com/me', {
    headers: { Authorization: `Bearer ${data.token}` },
  });
}

Running

k6 run script.js                                          # local
k6 run --vus 50 --duration 1m script.js                   # override options
k6 run --out json=results.json script.js                  # JSON output
k6 run --out experimental-prometheus-rw script.js         # Prometheus remote write
k6 cloud script.js                                        # run in k6 Cloud

Verify flag names with k6 run --help against your installed version.


CI integration

- run: |
    curl https://github.com/grafana/k6/releases/download/<version>/k6-<version>-linux-amd64.tar.gz | tar xz
    ./k6 run --out junit=k6-junit.xml tests/load/checkout.js
- if: always()
  uses: actions/upload-artifact@v4
  with:
    name: k6-results
    path: k6-junit.xml

For pass/fail gating, rely on the exit code (non-zero on threshold breach). For richer reporting, push metrics to Prometheus / InfluxDB / Datadog via the appropriate output, and visualize in Grafana.


Common Pitfalls

  • No thresholds — the run can't fail; it's just a benchmark, not a test.
  • VU-based executor with slow responses — actual throughput is lower than expected because VUs are blocked. Use arrival-rate executors for throughput-shaped goals.
  • No sleep() between iterations — VUs hammer the server faster than any real client. Add realistic think time, OR use an arrival-rate executor (which makes sleep less critical).
  • Running against production without owner sign-off — career-ending move. Always confirm with the service owner and the SRE team.
  • One scenario for everything — checkout and read-only browsing have different traffic shapes. Separate scenarios.
  • Asserting only on the happy path — capture error rate explicitly via http_req_failed.
  • Forgetting to tag URLs — http.get('https://staging.example.com/users/abc') and .../users/xyz show up as different aggregations. Either use URL templates or explicit tags.name.
  • Loading huge JSON in default instead of init — wastes memory per VU. Use init context (top-level) or SharedArray.
  • Skipping ramp-up — sudden 1000 VUs at t=0 is unrealistic and trips connection-pool issues that wouldn't fire in prod. Ramp.
  • Running tests too short — perf is noisy. Sub-minute runs give unstable results. Soak for at least a few minutes.

Task-Specific Questions

When helping with k6, ask:

  1. SLO target — p50/p95/p99 latency, RPS, error rate?
  2. Traffic shape — smoke, average load, peak, spike, soak?
  3. Where does the load run from (local CI runner, k6 Cloud, k6-operator on K8s)?
  4. Target environment — perf env, staging, isolated?
  5. Are there auth/login steps that should run in setup()?
  6. How is data parameterized — SharedArray, per-VU generation, fixture file?
  7. Where do results go — JUnit for CI, Prometheus/Grafana for dashboards, k6 Cloud, all?

Related Skills

  • jmeter — for teams with existing JMeter investment, or non-coder authors.
  • gatling — JVM perf-deep teams.
  • locust — Python-deep teams that want code-first.
  • artillery — lighter Node ecosystem alternative.
  • karate — karate-gatling reuses Karate features for load (Gatling-based).
  • grpc-testing — ghz is the gRPC-native equivalent; k6 has a gRPC module.
  • ci-test-orchestration — for wiring perf gates as required CI checks.
  • production-testing — for synthetic monitoring (similar shape, smaller scale, runs continuously).
  • test-strategy — for placing perf testing in the overall pyramid.

Keep looking

Skills are one crate of 628,853. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.