From qa-testing
Design and execute performance tests using k6, Artillery, and other tools. Use this skill when load testing APIs, stress testing systems, or establishing performance baselines. Activate when: performance test, load test, stress test, k6, artillery, benchmark, scalability testing.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qa-testing:performance-testingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Ensure your systems handle load with comprehensive performance testing.**
Ensure your systems handle load with comprehensive performance testing.
| Type | Purpose | Load Pattern |
|---|---|---|
| Load Test | Verify expected load | Steady, target users |
| Stress Test | Find breaking point | Ramp up until failure |
| Spike Test | Handle sudden traffic | Quick spike, then down |
| Soak Test | Find memory leaks | Steady load, long duration |
| Scalability | Auto-scaling validation | Step increases |
# macOS
brew install k6
# Docker
docker run -i grafana/k6 run - <script.js
// tests/load/basic.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 }, // Ramp up to 100 users
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests < 500ms
http_req_failed: ['rate<0.01'], // Error rate < 1%
},
};
export default function () {
const response = http.get('https://api.example.com/users');
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1); // Think time between requests
}
// tests/load/stress.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 300 },
{ duration: '5m', target: 300 },
{ duration: '2m', target: 400 }, // Breaking point?
{ duration: '5m', target: 400 },
{ duration: '10m', target: 0 }, // Recovery
],
};
export default function () {
const response = http.get('https://api.example.com/search?q=test');
check(response, {
'status is 200': (r) => r.status === 200,
'no errors': (r) => !r.body.includes('error'),
});
}
// tests/load/spike.js
export const options = {
stages: [
{ duration: '10s', target: 100 }, // Normal load
{ duration: '1m', target: 100 },
{ duration: '10s', target: 1400 }, // Spike to 1400 users
{ duration: '3m', target: 1400 }, // Stay at spike
{ duration: '10s', target: 100 }, // Scale down
{ duration: '3m', target: 100 }, // Recovery
{ duration: '10s', target: 0 },
],
};
// tests/load/authenticated.js
import http from 'k6/http';
import { check } from 'k6';
export function setup() {
// Login once, share token across VUs
const loginRes = http.post('https://api.example.com/auth/login', {
email: 'loadtest@example.com',
password: 'testpassword',
});
return { token: loginRes.json('token') };
}
export default function (data) {
const params = {
headers: {
Authorization: `Bearer ${data.token}`,
'Content-Type': 'application/json',
},
};
const response = http.get('https://api.example.com/profile', params);
check(response, {
'authenticated': (r) => r.status !== 401,
'has user data': (r) => r.json('user') !== undefined,
});
}
import http from 'k6/http';
import { Counter, Trend, Rate } from 'k6/metrics';
// Custom metrics
const orderSuccessRate = new Rate('order_success');
const orderDuration = new Trend('order_duration');
const ordersCreated = new Counter('orders_created');
export default function () {
const start = Date.now();
const response = http.post('https://api.example.com/orders', {
product_id: 123,
quantity: 1,
});
const duration = Date.now() - start;
orderDuration.add(duration);
orderSuccessRate.add(response.status === 201);
if (response.status === 201) {
ordersCreated.add(1);
}
}
npm install -g artillery
# tests/load/artillery.yml
config:
target: 'https://api.example.com'
phases:
- duration: 60
arrivalRate: 5
name: Warm up
- duration: 120
arrivalRate: 20
name: Sustained load
- duration: 60
arrivalRate: 50
name: Peak load
scenarios:
- name: 'Browse and search'
flow:
- get:
url: '/products'
capture:
- json: '$.products[0].id'
as: 'productId'
- get:
url: '/products/{{ productId }}'
- get:
url: '/search?q=test'
config:
target: 'https://api.example.com'
phases:
- duration: 120
arrivalRate: 10
payload:
path: 'users.csv'
fields:
- 'email'
- 'password'
scenarios:
- name: 'Authenticated user flow'
flow:
- post:
url: '/auth/login'
json:
email: '{{ email }}'
password: '{{ password }}'
capture:
- json: '$.token'
as: 'token'
- get:
url: '/profile'
headers:
Authorization: 'Bearer {{ token }}'
| Metric | Good | Warning | Critical |
|---|---|---|---|
| Response Time (p50) | <200ms | <500ms | >1000ms |
| Response Time (p95) | <500ms | <1000ms | >2000ms |
| Response Time (p99) | <1000ms | <2000ms | >5000ms |
| Error Rate | <0.1% | <1% | >5% |
| Throughput | >target | >80% target | <80% target |
## Performance Test Report
### Summary
- **Test Duration:** 10 minutes
- **Virtual Users:** 100 (peak)
- **Total Requests:** 50,000
- **Successful:** 49,850 (99.7%)
- **Failed:** 150 (0.3%)
### Response Times
| Percentile | Time |
|------------|------|
| p50 | 145ms |
| p90 | 320ms |
| p95 | 450ms |
| p99 | 890ms |
### Throughput
- **Avg:** 83 req/s
- **Peak:** 120 req/s
### Errors
| Code | Count | % |
|------|-------|---|
| 200 | 49,850 | 99.7% |
| 500 | 100 | 0.2% |
| 503 | 50 | 0.1% |
### Recommendations
1. Investigate 500 errors - likely database timeout
2. p99 approaching threshold - consider caching
3. Throughput meets requirements
# .github/workflows/performance.yml
name: Performance Tests
on:
schedule:
- cron: '0 2 * * *' # Nightly
workflow_dispatch:
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install k6
run: |
curl https://github.com/grafana/k6/releases/download/v0.47.0/k6-v0.47.0-linux-amd64.tar.gz -L | tar xvz
sudo mv k6-v0.47.0-linux-amd64/k6 /usr/local/bin/
- name: Run load test
run: k6 run tests/load/basic.js --out json=results.json
- name: Upload results
uses: actions/upload-artifact@v4
with:
name: k6-results
path: results.json
- name: Check thresholds
run: |
if grep -q '"thresholds":{".*":{"ok":false' results.json; then
echo "Performance thresholds failed!"
exit 1
fi
npx claudepluginhub latestaiagents/agent-skills --plugin qa-testingExecutes load, stress, spike, and soak tests with k6, Artillery, JMeter, Locust, and autocannon to find bottlenecks and check SLAs.
Creates and runs load tests with k6, JMeter, and Artillery for web apps and APIs. Validates performance under stress, spike, soak, scalability to detect bottlenecks.
Generates k6, Artillery, wrk scripts for API load/stress/soak tests to validate performance, identify bottlenecks, and establish baselines under configurable loads.