Claude-skill-registry load-testing
Auto-activates when user mentions load test, performance test, stress test, k6, Artillery, benchmark, or scalability testing. Expert in designing and executing performance tests.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/load-testing" ~/.claude/skills/majiayu000-claude-skill-registry-load-testing-4f39b3 && rm -rf "$T"
manifest:
skills/data/load-testing/SKILL.mdsource content
Load Testing & Performance Testing
Creates comprehensive load tests using k6, Artillery, and other tools to validate application performance under stress.
When This Activates
- User says: "load test this", "performance test", "stress test", "benchmark"
- User mentions: "k6", "Artillery", "JMeter", "Gatling", "scalability"
- Performance validation needed
- Pre-production testing
- Capacity planning questions
Load Testing Tools Comparison
k6 (Recommended)
- Best for: Modern apps, APIs, microservices
- Language: JavaScript/TypeScript
- Pros: Cloud-native, great metrics, scriptable
- Use when: Testing REST APIs, GraphQL, WebSocket
Artillery
- Best for: Quick tests, CI/CD integration
- Language: YAML + JavaScript
- Pros: Simple config, plugins, HTTP/WS/Socket.io
- Use when: Simple scenarios, rapid testing
Apache JMeter
- Best for: Enterprise apps, complex scenarios
- Language: GUI-based + Java
- Pros: Mature, extensive protocols, GUI
- Use when: Legacy systems, complex workflows
k6 Load Testing
Basic Load Test
// load-test.js import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend, Counter } from 'k6/metrics'; // Custom metrics const errorRate = new Rate('errors'); const checkDuration = new Trend('check_duration'); const requests = new Counter('requests'); // Test configuration export const options = { stages: [ { duration: '2m', target: 10 }, // Ramp up to 10 users { duration: '5m', target: 10 }, // Stay at 10 users { duration: '2m', target: 50 }, // Ramp up to 50 users { duration: '5m', target: 50 }, // Stay at 50 users { 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', 'p(99)<1000'], // 95% < 500ms, 99% < 1s http_req_failed: ['rate<0.01'], // Error rate < 1% errors: ['rate<0.1'], // Custom error rate < 10% }, }; const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000'; const API_TOKEN = __ENV.API_TOKEN || ''; export default function () { const params = { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${API_TOKEN}`, }, tags: { name: 'GetUsers' }, }; // GET request let response = http.get(`${BASE_URL}/api/users`, params); const checkResult = check(response, { 'status is 200': (r) => r.status === 200, 'response time < 500ms': (r) => r.timings.duration < 500, 'has users': (r) => JSON.parse(r.body).data.length > 0, }); errorRate.add(!checkResult); checkDuration.add(response.timings.duration); requests.add(1); // POST request const payload = JSON.stringify({ name: 'Test User', email: `test-${Date.now()}@example.com`, }); response = http.post(`${BASE_URL}/api/users`, payload, params); check(response, { 'user created': (r) => r.status === 201, }); sleep(1); // Think time between requests } export function handleSummary(data) { return { 'summary.json': JSON.stringify(data), stdout: textSummary(data, { indent: ' ', enableColors: true }), }; }
Advanced Scenarios
// advanced-test.js import http from 'k6/http'; import { check, group, sleep } from 'k6'; import { SharedArray } from 'k6/data'; import papaparse from 'https://jslib.k6.io/papaparse/5.1.1/index.js'; // Load test data const users = new SharedArray('users', function () { return papaparse.parse(open('./users.csv'), { header: true }).data; }); export const options = { scenarios: { // Scenario 1: Constant load constant_load: { executor: 'constant-vus', vus: 50, duration: '5m', }, // Scenario 2: Ramping load ramping_load: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '5m', target: 100 }, { duration: '10m', target: 100 }, { duration: '5m', target: 0 }, ], gracefulRampDown: '30s', }, // Scenario 3: Stress test stress_test: { executor: 'ramping-arrival-rate', startRate: 50, timeUnit: '1s', preAllocatedVUs: 500, maxVUs: 1000, stages: [ { duration: '2m', target: 100 }, { duration: '5m', target: 200 }, { duration: '2m', target: 500 }, { duration: '5m', target: 500 }, { duration: '2m', target: 0 }, ], }, }, }; export default function () { const user = users[Math.floor(Math.random() * users.length)]; group('User Flow', function () { // 1. Login group('Login', function () { const loginRes = http.post(`${BASE_URL}/api/auth/login`, JSON.stringify({ email: user.email, password: user.password, }), { headers: { 'Content-Type': 'application/json' }, }); check(loginRes, { 'login successful': (r) => r.status === 200, }); const token = loginRes.json('token'); // 2. Get profile group('Get Profile', function () { const profileRes = http.get(`${BASE_URL}/api/profile`, { headers: { 'Authorization': `Bearer ${token}`, }, }); check(profileRes, { 'profile loaded': (r) => r.status === 200, }); }); // 3. Create post group('Create Post', function () { const postRes = http.post(`${BASE_URL}/api/posts`, JSON.stringify({ title: 'Load Test Post', content: 'This is a test post from k6', }), { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, }); check(postRes, { 'post created': (r) => r.status === 201, }); }); }); }); sleep(Math.random() * 3 + 2); // Random think time 2-5s }
GraphQL Load Test
// graphql-test.js import http from 'k6/http'; import { check } from 'k6'; export const options = { vus: 50, duration: '5m', thresholds: { http_req_duration: ['p(95)<1000'], }, }; const GRAPHQL_ENDPOINT = `${__ENV.BASE_URL}/graphql`; export default function () { const query = ` query GetPosts($first: Int!) { posts(first: $first) { edges { node { id title author { name } } } } } `; const variables = { first: 20 }; const response = http.post( GRAPHQL_ENDPOINT, JSON.stringify({ query, variables }), { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${__ENV.API_TOKEN}`, }, } ); check(response, { 'no errors': (r) => !r.json('errors'), 'has data': (r) => r.json('data.posts.edges.length') > 0, }); }
Artillery Load Testing
Basic Configuration
# artillery.yml config: target: "http://localhost:3000" phases: - duration: 60 arrivalRate: 10 # 10 users per second name: "Warm up" - duration: 300 arrivalRate: 50 # 50 users per second name: "Sustained load" - duration: 120 arrivalRate: 100 # 100 users per second name: "Spike" processor: "./helpers.js" variables: apiToken: "{{ $processEnvironment.API_TOKEN }}" plugins: expect: {} metrics-by-endpoint: {} scenarios: - name: "User Registration and Login" flow: - post: url: "/api/auth/register" json: email: "user-{{ $randomString() }}@example.com" name: "Test User" password: "password123" capture: - json: "$.token" as: "authToken" expect: - statusCode: 201 - contentType: json - hasProperty: token - get: url: "/api/profile" headers: Authorization: "Bearer {{ authToken }}" expect: - statusCode: 200 - post: url: "/api/posts" headers: Authorization: "Bearer {{ authToken }}" json: title: "Test Post" content: "This is a test" expect: - statusCode: 201 - name: "Browse Posts" weight: 3 flow: - get: url: "/api/posts?page=1&limit=20" expect: - statusCode: 200 - hasProperty: data - think: 2 # Wait 2 seconds - get: url: "/api/posts/{{ $randomNumber(1, 100) }}" expect: - statusCode: [200, 404]
// helpers.js module.exports = { generateRandomEmail, logResponse, }; function generateRandomEmail(context, events, done) { context.vars.email = `user-${Date.now()}@example.com`; return done(); } function logResponse(requestParams, response, context, ee, next) { if (response.statusCode >= 400) { console.error(`Error ${response.statusCode}: ${response.body}`); } return next(); }
Running Tests
# k6 k6 run load-test.js k6 run --vus 100 --duration 5m load-test.js k6 run --env BASE_URL=https://api.example.com load-test.js # k6 Cloud k6 cloud load-test.js # Artillery artillery run artillery.yml artillery run --target https://api.example.com artillery.yml # Artillery with environment API_TOKEN=xxx artillery run artillery.yml # Generate HTML report artillery run --output report.json artillery.yml artillery report report.json
CI/CD Integration
GitHub Actions
# .github/workflows/load-test.yml name: Load Test on: schedule: - cron: '0 2 * * *' # Run daily at 2 AM workflow_dispatch: jobs: load-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Run k6 load test uses: grafana/k6-action@v0.3.0 with: filename: tests/load-test.js cloud: true token: ${{ secrets.K6_CLOUD_TOKEN }} env: BASE_URL: ${{ secrets.STAGING_URL }} API_TOKEN: ${{ secrets.API_TOKEN }} - name: Upload results uses: actions/upload-artifact@v3 if: always() with: name: load-test-results path: summary.json
Performance Benchmarking
// benchmark.js import http from 'k6/http'; import { check } from 'k6'; export const options = { scenarios: { baseline: { executor: 'constant-vus', vus: 1, duration: '1m', tags: { test_type: 'baseline' }, }, }, }; export default function () { const endpoints = [ '/api/users', '/api/posts', '/api/comments', ]; endpoints.forEach(endpoint => { const response = http.get(`${BASE_URL}${endpoint}`); check(response, { [`${endpoint} status 200`]: (r) => r.status === 200, }); }); } export function handleSummary(data) { const baseline = { endpoints: {}, timestamp: new Date().toISOString(), }; for (const [name, metric] of Object.entries(data.metrics)) { if (name.includes('http_req_duration')) { baseline.endpoints[name] = { avg: metric.values.avg, p95: metric.values['p(95)'], p99: metric.values['p(99)'], }; } } return { 'baseline.json': JSON.stringify(baseline, null, 2), }; }
Analyzing Results
Key Metrics to Monitor
-
Response Time
- Average, p95, p99
- Target: p95 < 500ms, p99 < 1s
-
Error Rate
- HTTP 4xx, 5xx errors
- Target: < 1%
-
Throughput
- Requests per second
- Target: Based on requirements
-
Concurrent Users
- Maximum sustainable load
- Target: Based on traffic projections
Identifying Bottlenecks
// Find slow endpoints const slowRequests = data.metrics.http_req_duration.values; console.log(`Average: ${slowRequests.avg}ms`); console.log(`p95: ${slowRequests['p(95)']}ms`); console.log(`p99: ${slowRequests['p(99)']}ms`); // Check error patterns const failedRequests = data.metrics.http_req_failed.values.rate; if (failedRequests > 0.01) { console.error(`Error rate: ${failedRequests * 100}%`); }
Best Practices
Test Types
-
Smoke Test: 1-2 VUs, 1-2 minutes
- Verify system works under minimal load
-
Load Test: Expected normal/peak load, 5-15 minutes
- Validate performance under typical conditions
-
Stress Test: Beyond peak load, gradually increase
- Find breaking point
-
Spike Test: Sudden large increase, then drop
- Test auto-scaling, recovery
-
Soak Test: Moderate load, 1+ hours
- Find memory leaks, degradation
Checklist
- Define performance requirements (SLAs)
- Identify critical user flows
- Prepare test data (users, content)
- Set up realistic scenarios
- Configure appropriate thresholds
- Test in staging environment first
- Monitor system resources during tests
- Analyze results against baselines
- Document bottlenecks and fixes
- Run tests regularly (CI/CD)
- Compare results over time
Design load tests, execute tests, analyze results, provide optimization recommendations.