← All projects

Featured project 7 · JavaScript · Performance

k6 Performance Testing

Smoke, load, stress, spike and soak tests with Grafana k6 for a shop API kept in the repository: service level objectives as thresholds that fail the CI job, and a stress test that found a real bottleneck.

Live CI status of k6 Performance Testing

View source code View documentation View latest CI runs View test report

Purpose

Shows how I approach performance testing: one test per question, pass/fail criteria agreed up front, realistic workload models, and results that lead to a fix. The app under test, a small shop API, is part of the repository, so the tests never put load on someone else's server and every CI job measures its own copy.

Technology stack

AreaTools
Load testingGrafana k6 2.3 (scenarios, executors, checks, thresholds, custom metrics)
ScriptsJavaScript ES modules (k6 runtime)
App under testQuickShop: Node.js 24 HTTP API, no dependencies, with functional tests
Reportsk6 web dashboard (HTML), Markdown summary per run, JSON of every metric
Code quality and CIESLint, Prettier, GitHub Actions: lint, smoke gate, load/stress/spike in parallel, nightly soak

Architecture overview

k6/tests/*.js     scenarios (who, how many, which executor) + thresholds (SLOs)
      │
k6/lib/flows.js   journeys browse() and buy(): checks, think time, business metrics
k6/lib/api.js     one function per endpoint, stable name tag per request
k6/lib/config.js  BASE_URL, PROFILE (ci | full), THINK
      │
QuickShop (app/)  started by each CI job; checkout limited by 8 "database connections"

Key engineering decisions

Implemented features

Setup and execution

npm install
npm start                               # QuickShop on http://localhost:3000
k6 run k6/tests/smoke.js
k6 run k6/tests/load.js
k6 run -e THINK=0 k6/tests/stress.js
k6 run -e PROFILE=full k6/tests/load.js  # realistic size and duration

Sample test results

k6 dashboard of the first stress test: response times climb without limit
Stress test before the fix: past the limit, checkout times climb until the abort threshold stops the run.
k6 dashboard of the stress test after the fix: response times level off
The same test after adding load shedding: response times level off and every answer is correct.
Markdown summary of the regression demo with the failed checkout threshold
Regression demo: one threshold fails, so the run fails.

CI workflow

On every push and pull request: ESLint, Prettier and the app's functional tests, then the smoke test as the gate, then the load, stress and spike tests as three parallel jobs, each with its own copy of the app. The soak test runs nightly. Each job writes its result table to the run summary and keeps the HTML dashboard as an artifact.

Known limitations and future improvements

Limitations

Next steps