| name | load-testing-patterns |
| description | k6 script templates, load profiles, response time thresholds, SLO validation, and performance testing strategies. |
Load Testing Patterns
Performance validation with k6 for SLO-driven load testing.
Basic k6 Script Template
import http from 'k6/http'
import { check, sleep } from 'k6'
import { Rate, Trend } from 'k6/metrics'
const errorRate = new Rate('errors')
const loginDuration = new Trend('login_duration')
export const options = {
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
errors: ['rate<0.05'],
},
scenarios: {
smoke: {
executor: 'constant-vus',
vus: 5,
duration: '1m',
}
}
}
export default function () {
const res = http.get('https://api.example.com/health')
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body has status': (r) => JSON.parse(r.body).status === 'ok',
}) || errorRate.add(1)
sleep(1)
}
Load Profiles
export const options = {
scenarios: {
smoke: {
executor: 'constant-vus',
vus: 3,
duration: '1m',
},
load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 50 },
{ duration: '5m', target: 50 },
{ duration: '2m', target: 0 },
],
},
stress: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
],
},
: {
: ,
: ,
: [
{ : , : },
{ : , : },
{ : , : },
{ : , : },
{ : , : },
],
},
: {
: ,
: ,
: ,
},
}
}
Realistic User Scenarios
import http from 'k6/http'
import { check, group, sleep } from 'k6'
const BASE_URL = __ENV.BASE_URL || 'https://api.example.com'
export default function () {
let token
group('01_login', () => {
const res = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: `user${__VU}@test.com`,
password: 'testpass123'
}), { headers: { 'Content-Type': 'application/json' } })
check(res, { 'login successful': (r) => r.status === 200 })
token = JSON.parse(res.body).token
})
sleep(Math.random() * 3 + 1)
group('02_browse_products', {
headers = { : }
listRes = http.(, { headers })
(listRes, { : r. === })
products = .(listRes.).
(products. > ) {
product = products[.(.() * products.)]
detailRes = http.(, { headers })
(detailRes, { : r. === })
}
})
(.() * + )
(, {
res = http.(, .({
: ,
:
}), {
: {
: ,
:
}
})
(res, { : r. === })
})
}
SLO Validation
export const options = {
thresholds: {
http_req_failed: ['rate<0.001'],
http_req_duration: [
'p(50)<100',
'p(95)<500',
'p(99)<1000',
],
'http_req_duration{name:login}': ['p(95)<800'],
'http_req_duration{name:get_products}': ['p(95)<200'],
'http_req_duration{name:checkout}': ['p(95)<2000'],
http_reqs: ['rate>1000'],
}
}
export default function () {
http.get(`${BASE_URL}/products`, { tags: { name: 'get_products' } })
http.post(`${BASE_URL}/auth/login`, payload, { tags: { name: 'login' } })
}
CI Integration
name: Load Test
on:
pull_request:
branches: [main]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: grafana/k6-action@v0.3.1
with:
filename: tests/load/smoke.js
env:
BASE_URL: ${{ secrets.STAGING_URL }}
Checklist
Anti-Patterns
- Testing only happy paths: include error scenarios (404, 429, 500)
- No think time: unrealistic request rate, not how users behave
- Testing against production without traffic control (use staging)
- Single endpoint tests: real users hit multiple endpoints per session
- Ignoring connection time: p95 duration hides DNS/TLS overhead
- Hardcoded test data: VUs sharing same user account causes contention