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.md
source 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

  1. Response Time

    • Average, p95, p99
    • Target: p95 < 500ms, p99 < 1s
  2. Error Rate

    • HTTP 4xx, 5xx errors
    • Target: < 1%
  3. Throughput

    • Requests per second
    • Target: Based on requirements
  4. 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

  1. Smoke Test: 1-2 VUs, 1-2 minutes

    • Verify system works under minimal load
  2. Load Test: Expected normal/peak load, 5-15 minutes

    • Validate performance under typical conditions
  3. Stress Test: Beyond peak load, gradually increase

    • Find breaking point
  4. Spike Test: Sudden large increase, then drop

    • Test auto-scaling, recovery
  5. 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.