Skip to main content

Performance Testing: Load Testing with k6 and Artillery

Created: February 23, 2026 Larry Qu 3 min read

Introduction

Performance testing ensures your system can handle expected load. This guide covers load testing with k6 and Artillery - modern, scriptable tools for testing API and web performance.


Testing Types

┌─────────────────────────────────────────────────────────────┐
│                Performance Testing Types                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Load Testing                                              │
│  • Normal expected load                                    │
│  • Peak load scenarios                                     │
│  • Duration: 10-30 minutes                                 │
│                                                             │
│  Stress Testing                                           │
│  • Beyond normal capacity                                  │
│  • Find breaking point                                     │
│  • Test recovery                                          │
│                                                             │
│  Spike Testing                                            │
│  • Sudden traffic increase                                │
│  • How fast can you scale?                                │
│                                                             │
│  Soak Testing                                             │
│  • Extended period (hours)                                │
│  • Find memory leaks                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

k6

Setup

# Install k6
brew install k6
# or
npm install -D k6

# Run test
k6 run test.js

Basic Script

// test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  stages: [
    { duration: '30s', target: 10 },   // Ramp up
    { duration: '1m', target: 10 },  // Stay at 10 users
    { duration: '30s', target: 0 },  // Ramp down
  ],
};

export default function () {
  const res = http.get('https://api.example.com/users');
  
  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });
  
  sleep(1);
}

Advanced Options

export const options = {
  // Virtual users
  vus: 10,
  duration: '2m',
  
  // Thresholds (fail test if exceeded)
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95% under 500ms
    http_req_failed: ['rate<0.01'],  // Error rate < 1%
  },
  
  // Setup and teardown
  setup: () => { /* run once */ },
  teardown: () => {/* run once */},
};

export default function () {
  // Group related requests
  group('User flow', () => {
    const login = http.post('/login', JSON.stringify({email: '[email protected]'}));
    
    check(login, { 'login success': (r) => r.status === 200 });
    
    const user = http.get('/profile', {
      headers: { 'Authorization': `Bearer ${login.json().token}` },
    });
    
    check(user, { 'get profile': (r) => r.status === 200 });
  });
}

Artillery

Setup

npm install -D artillery

YAML Configuration

# test-config.yml
config:
  target: "https://api.example.com"
  phases:
    - duration: 60
      arrivalRate: 5
      name: "Warm up"
    - duration: 120
      arrivalRate: 20
      name: "Sustained load"
    - duration: 30
      arrivalRate: 50
      name: "Stress test"
  
  plugins:
    expect: {}
  
  processor: "./handlers.js"

scenarios:
  - name: "Get users"
    flow:
      - get:
          url: "/api/users"
          capture:
            - json: "$.users[0].id"
              as: "userId"
          expect:
            - statusCode: 200
            - contentType: json
    
  - name: "Create user"
    flow:
      - post:
          url: "/api/users"
          json:
            name: "Test User"
            email: "[email protected]"
          expect:
            - statusCode: 201

Running

# Run test
npx artillery run test-config.yml

# Quick test
npx artillery quick --duration 10 --rate 10 https://api.example.com

Metrics to Track

# Key performance metrics
metrics:
  response_time:
    - "p50: Median response time"
    - "p95: 95th percentile"
    - "p99: 99th percentile"
    
  throughput:
    - "requests per second"
    - "transactions per second"
    
  errors:
    - "error rate"
    - "timeout rate"
    
  resources:
    - "CPU usage"
    - "Memory usage"
    - "Network I/O"

CI Integration

# GitHub Actions
- name: Load Test
  run: |
    k6 run \
      --out json=k6-results.json \
      --summary-export=k6-summary.json \
      test.js
  env:
    K6_CLOUD_TOKEN: ${{ secrets.K6_CLOUD_TOKEN }}

Key Takeaways

  • k6 - Scriptable, JavaScript, great for developers
  • Artillery - YAML-based, simple configuration
  • Test early - Catch performance issues in CI
  • Set thresholds - Fail builds on regressions

External Resources

Comments

Share this article

Scan to read on mobile