K6 Herramienta Moderna de Pruebas de Rendimiento

Ejemplos completos de pruebas de rendimiento K6 incluyendo pruebas de carga, pruebas de estrés, pruebas de rendimiento de API y patrones avanzados para aplicaciones modernas

💻 Configuración Básica de K6 javascript

🟢 simple

Configuración completa de proyecto K6 con archivos de configuración, estructura básica de pruebas y fundamentos de pruebas de rendimiento

⏱️ 30 min 🏷️ k6, performance, setup, configuration
Prerequisites: JavaScript basics, HTTP protocols, Performance testing concepts
// K6 Performance Testing - Basic Setup and Configuration

// 1. package.json - Dependencies
{
  "name": "k6-performance-tests",
  "version": "1.0.0",
  "description": "Performance testing suite with K6",
  "scripts": {
    "test": "k6 run",
    "test:smoke": "k6 run --vus 10 --duration 30s tests/smoke.js",
    "test:load": "k6 run --vus 50 --duration 2m tests/load.js",
    "test:stress": "k6 run --vus 100 --duration 5m tests/stress.js",
    "test:spike": "k6 run --vus 200 --duration 10s tests/spike.js",
    "test:soak": "k6 run --vus 30 --duration 30m tests/soak.js",
    "report:html": "k6 run --out json=results.json tests/load.js && k6-reporter --json results.json"
  },
  "devDependencies": {
    "k6": "^0.49.0",
    "k6-reporter": "^2.2.0"
  }
}

// 2. k6.config.js - Global Configuration
export const options = {
  // Test execution options
  vus: 10, // Virtual users
  duration: '30s', // Test duration

  // Stages for ramp-up/ramp-down
  stages: [
    { duration: '10s', target: 10 }, // Ramp up
    { duration: '20s', target: 10 }, // Stay
    { duration: '10s', target: 0 },  // Ramp down
  ],

  // Thresholds
  thresholds: {
    http_req_duration: ['p(95)<500'], // 95% of requests under 500ms
    http_req_failed: ['rate<0.1'],     // Error rate under 10%
    checks: ['rate>0.95'],             // 95% of checks should pass
  },

  // Headers
  headers: {
    'Content-Type': 'application/json',
    'User-Agent': 'k6-performance-test/1.0',
  },
};

// 3. tests/smoke.js - Basic Smoke Test
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
  vus: 5,
  duration: '30s',
  thresholds: {
    http_req_duration: ['p(95)<200'],
    http_req_failed: ['rate<0.01'],
  },
};

export default function () {
  // Test API endpoint
  const url = 'https://jsonplaceholder.typicode.com/posts/1';

  const response = http.get(url);

  // Basic checks
  check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 200ms': (r) => r.timings.duration < 200,
    'content-type is correct': (r) => r.headers['Content-Type'] === 'application/json; charset=utf-8',
  });

  sleep(1);
}

// 4. lib/config.js - Configuration Library
export const BASE_URL = 'https://api.example.com';
export const API_VERSION = 'v1';

export const DEFAULT_HEADERS = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
};

export const THRESHOLDS = {
  SMOKE: {
    http_req_duration: ['p(95)<200'],
    http_req_failed: ['rate<0.01'],
  },
  LOAD: {
    http_req_duration: ['p(95)<500'],
    http_req_failed: ['rate<0.05'],
  },
  STRESS: {
    http_req_duration: ['p(95)<1000'],
    http_req_failed: ['rate<0.1'],
  },
};

// 5. lib/utils.js - Utility Functions
import http from 'k6/http';
import { check } from 'k6';

export class ApiClient {
  constructor(baseUrl, headers = {}) {
    this.baseUrl = baseUrl;
    this.headers = { ...DEFAULT_HEADERS, ...headers };
  }

  get(path, params = {}) {
    const url = this.buildUrl(path, params);
    return http.get(url, { headers: this.headers });
  }

  post(path, data = {}) {
    const url = this.buildUrl(path);
    const payload = JSON.stringify(data);
    const params = {
      headers: this.headers,
    };
    return http.post(url, payload, params);
  }

  put(path, data = {}) {
    const url = this.buildUrl(path);
    const payload = JSON.stringify(data);
    const params = {
      headers: this.headers,
    };
    return http.put(url, payload, params);
  }

  delete(path) {
    const url = this.buildUrl(path);
    return http.del(url, null, { headers: this.headers });
  }

  buildUrl(path, params = {}) {
    const url = new URL(path, this.baseUrl);
    Object.keys(params).forEach(key => {
      url.searchParams.append(key, params[key]);
    });
    return url.toString();
  }

  // Common check patterns
  checkSuccess(response, customChecks = {}) {
    const defaultChecks = {
      'status is successful': (r) => r.status >= 200 && r.status < 300,
      'response time < 1s': (r) => r.timings.duration < 1000,
    };

    return check(response, { ...defaultChecks, ...customChecks });
  }
}

💻 Patrones Avanzados de Pruebas de Carga javascript

🟡 intermediate ⭐⭐⭐

Patrones completos de pruebas de carga incluyendo ramp-up gradual, pruebas de pico, pruebas de soaking y pruebas de estrés

⏱️ 45 min 🏷️ k6, load testing, stress testing, spike testing
Prerequisites: K6 basics, Performance testing concepts, JavaScript
// K6 Advanced Load Testing Patterns

import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

// Custom metrics
export let errorRate = new Rate('errors');
export let responseTime = new Trend('response_time');

// 1. Load Testing - Gradual Ramp Up
export function loadTest() {
  export let options = {
    stages: [
      { duration: '2m', target: 20 },   // Ramp up to 20 users
      { duration: '5m', target: 20 },   // Stay at 20 users
      { duration: '2m', target: 50 },   // Ramp up to 50 users
      { duration: '5m', target: 50 },   // Stay at 50 users
      { duration: '2m', target: 0 },    // Ramp down
    ],
    thresholds: {
      http_req_duration: ['p(95)<500'],
      http_req_failed: ['rate<0.05'],
      errors: ['rate<0.05'],
    },
  };
}

export default function () {
  const response = http.get('https://api.example.com/users', {
    headers: { 'Accept': 'application/json' },
  });

  const success = check(response, {
    'status is 200': (r) => r.status === 200,
    'response time < 500ms': (r) => r.timings.duration < 500,
  });

  errorRate.add(!success);
  responseTime.add(response.timings.duration);

  sleep(Math.random() * 3 + 1); // Random sleep 1-4s
}

// 2. Spike Testing - Sudden Traffic Surge
export function spikeTest() {
  export let options = {
    stages: [
      { duration: '1m', target: 10 },   // Normal load
      { duration: '30s', target: 100 }, // Spike
      { duration: '1m', target: 0 },    // Cool down
    ],
    thresholds: {
      http_req_duration: ['p(95)<1000'],
      http_req_failed: ['rate<0.1'],
    },
  };
}

export default function () {
  const payload = JSON.stringify({
    name: `User_${__VU}`,
    email: `user${__VU}@example.com`,
  });

  const response = http.post('https://api.example.com/users', payload, {
    headers: { 'Content-Type': 'application/json' },
  });

  check(response, {
    'status is 201': (r) => r.status === 201,
    'user created': (r) => JSON.parse(r.body).id !== undefined,
  });
}

// 3. Soak Testing - Extended Duration Test
export function soakTest() {
  export let options = {
    stages: [
      { duration: '5m', target: 5 },    // Ramp up
      { duration: '1h', target: 5 },    // Soak for 1 hour
      { duration: '5m', target: 0 },    // Ramp down
    ],
    thresholds: {
      http_req_duration: ['p(95)<300'],
      http_req_failed: ['rate<0.01'],
      checks: ['rate>0.99'],
    },
  };
}

export default function () {
  // Simulate user journey
  const responses = http.batch([
    ['GET', 'https://api.example.com/products'],
    ['GET', 'https://api.example.com/products/1'],
    ['GET', 'https://api.example.com/users/1'],
  ]);

  responses.forEach(response => {
    check(response, {
      'status is 200': (r) => r.status === 200,
    });
  });

  sleep(Math.random() * 5 + 2); // Random wait 2-7s
}

// 4. Stress Testing - Finding Breaking Point
export function stressTest() {
  export let options = {
    stages: [
      { duration: '2m', target: 20 },   // Warm up
      { duration: '5m', target: 50 },   // Load
      { duration: '5m', target: 100 },  // High load
      { duration: '5m', target: 200 },  // Stress
      { duration: '5m', target: 300 },  // Peak stress
      { duration: '5m', target: 0 },    // Recovery
    ],
    thresholds: {
      http_req_duration: ['p(95)<2000'],
      http_req_failed: ['rate<0.2'],
    },
  };
}

export default function () {
  const response = http.get('https://api.example.com/heavy-endpoint', {
    timeout: '10s',
  });

  check(response, {
    'status is successful': (r) => r.status >= 200 && r.status < 300,
    'response time reasonable': (r) => r.timings.duration < 5000,
  });
}

// 5. API Load Testing with Different Scenarios
export function apiLoadTest() {
  export let options = {
    vus: 50,
    duration: '10m',
    thresholds: {
      'http_req_duration{type:read}': ['p(95)<300'],
      'http_req_duration{type:write}': ['p(95)<800'],
      'http_req_failed{type:read}': ['rate<0.01'],
      'http_req_failed{type:write}': ['rate<0.05'],
    },
  };
}

export default function () {
  // 80% read operations, 20% write operations
  if (Math.random() < 0.8) {
    // Read operation
    const response = http.get('https://api.example.com/posts', {
      tags: { type: 'read' },
    });

    check(response, {
      'GET posts status': (r) => r.status === 200,
      'posts returned': (r) => JSON.parse(r.body).length > 0,
    });
  } else {
    // Write operation
    const payload = JSON.stringify({
      title: `Test Post ${__VU}`,
      content: 'This is a test post created during load testing.',
      authorId: Math.floor(Math.random() * 100) + 1,
    });

    const response = http.post('https://api.example.com/posts', payload, {
      headers: { 'Content-Type': 'application/json' },
      tags: { type: 'write' },
    });

    check(response, {
      'POST posts status': (r) => r.status === 201,
      'post created': (r) => JSON.parse(r.body).id !== undefined,
    });
  }

  sleep(1);
}

💻 Pruebas de Rendimiento de API con K6 javascript

🔴 complex ⭐⭐⭐⭐

Pruebas completas de API incluyendo REST API, GraphQL, autenticación y pruebas basadas en datos

⏱️ 60 min 🏷️ k6, api testing, rest, graphql
Prerequisites: K6 advanced, API testing, GraphQL, Authentication
// K6 API Performance Testing

import http from 'k6/http';
import { check, sleep, group } from 'k6';
import { Rate } from 'k6/metrics';

// Custom metrics
export let errorRate = new Rate('errors');
export let apiErrors = new Rate('api_errors');

// Configuration
const BASE_URL = 'https://api.example.com';
const AUTH_TOKEN = 'your-api-token-here';

// 1. REST API Testing Suite
export function restApiTest() {
  export let options = {
    vus: 20,
    duration: '5m',
    thresholds: {
      http_req_duration: ['p(95)<500'],
      http_req_failed: ['rate<0.02'],
      api_errors: ['rate<0.01'],
    },
  };

  export default function () {
    group('User Authentication', () => {
      const loginResponse = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
        email: '[email protected]',
        password: 'testpassword',
      }), {
        headers: { 'Content-Type': 'application/json' },
      });

      const loginSuccess = check(loginResponse, {
        'login status is 200': (r) => r.status === 200,
        'has token': (r) => JSON.parse(r.body).token !== undefined,
      });

      if (loginSuccess) {
        const token = JSON.parse(loginResponse.body).token;

        group('Authenticated API Calls', () => {
          // Get user profile
          const profileResponse = http.get(`${BASE_URL}/users/profile`, {
            headers: {
              'Authorization': `Bearer ${token}`,
              'Content-Type': 'application/json',
            },
          });

          check(profileResponse, {
            'profile status is 200': (r) => r.status === 200,
            'has user data': (r) => JSON.parse(r.body).id !== undefined,
          });

          // Update user profile
          const updateResponse = http.put(`${BASE_URL}/users/profile`, JSON.stringify({
            name: 'Updated Test User',
          }), {
            headers: {
              'Authorization': `Bearer ${token}`,
              'Content-Type': 'application/json',
            },
          });

          check(updateResponse, {
            'update status is 200': (r) => r.status === 200,
            'profile updated': (r) => JSON.parse(r.body).name === 'Updated Test User',
          });
        });
      }

      apiErrors.add(!loginSuccess);
    });

    sleep(1);
  }
}

// 2. GraphQL API Testing
export function graphqlApiTest() {
  export let options = {
    vus: 15,
    duration: '3m',
    thresholds: {
      http_req_duration: ['p(95)<600'],
      http_req_failed: ['rate<0.03'],
    },
  };

  const GRAPHQL_URL = 'https://api.example.com/graphql';

  export default function () {
    // Query example
    const query = `
      query GetUserPosts($userId: ID!) {
        user(id: $userId) {
          id
          name
          email
          posts {
            id
            title
            content
            createdAt
          }
        }
      }
    `;

    const variables = {
      userId: Math.floor(Math.random() * 100) + 1,
    };

    const response = http.post(GRAPHQL_URL, JSON.stringify({
      query: query,
      variables: variables,
    }), {
      headers: { 'Content-Type': 'application/json' },
    });

    check(response, {
      'GraphQL status is 200': (r) => r.status === 200,
      'has data': (r) => JSON.parse(r.body).data !== undefined,
      'no GraphQL errors': (r) => JSON.parse(r.body).errors === undefined,
    });

    sleep(2);
  }
}

// 3. Data-Driven API Testing
export function dataDrivenApiTest() {
  export let options = {
    vus: 10,
    duration: '2m',
  };

  // Test data
  const testUsers = [
    { email: '[email protected]', password: 'password1', role: 'admin' },
    { email: '[email protected]', password: 'password2', role: 'user' },
    { email: '[email protected]', password: 'password3', role: 'moderator' },
  ];

  export default function () {
    const user = testUsers[Math.floor(Math.random() * testUsers.length)];

    group('User Authentication', () => {
      const response = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
        email: user.email,
        password: user.password,
      }), {
        headers: { 'Content-Type': 'application/json' },
        tags: { user_role: user.role },
      });

      const success = check(response, {
        'login successful': (r) => r.status === 200,
        'token received': (r) => JSON.parse(r.body).token !== undefined,
        'correct role': (r) => JSON.parse(r.body).user.role === user.role,
      });

      if (success) {
        const token = JSON.parse(response.body).token;

        // Perform role-specific actions
        if (user.role === 'admin') {
          adminActions(token);
        } else if (user.role === 'user') {
          userActions(token);
        } else if (user.role === 'moderator') {
          moderatorActions(token);
        }
      }

      errorRate.add(!success);
    });

    sleep(3);
  }
}

// Role-specific actions
function adminActions(token) {
  group('Admin Actions', () => {
    const response = http.get(`${BASE_URL}/admin/dashboard`, {
      headers: { 'Authorization': `Bearer ${token}` },
      tags: { action: 'admin_dashboard' },
    });

    check(response, {
      'dashboard accessible': (r) => r.status === 200,
      'has admin data': (r) => JSON.parse(r.body).adminStats !== undefined,
    });
  });
}

function userActions(token) {
  group('User Actions', () => {
    const response = http.get(`${BASE_URL}/user/profile`, {
      headers: { 'Authorization': `Bearer ${token}` },
      tags: { action: 'user_profile' },
    });

    check(response, {
      'profile accessible': (r) => r.status === 200,
      'has user data': (r) => JSON.parse(r.body).profile !== undefined,
    });
  });
}

function moderatorActions(token) {
  group('Moderator Actions', () => {
    const response = http.get(`${BASE_URL}/moderator/content`, {
      headers: { 'Authorization': `Bearer ${token}` },
      tags: { action: 'moderate_content' },
    });

    check(response, {
      'content accessible': (r) => r.status === 200,
      'has content list': (r) => Array.isArray(JSON.parse(r.body).content),
    });
  });
}

// 4. API Performance Testing with Multiple Endpoints
export function multiEndpointApiTest() {
  export let options = {
    vus: 30,
    duration: '10m',
    thresholds: {
      'http_req_duration{endpoint:products}': ['p(95)<400'],
      'http_req_duration{endpoint:orders}': ['p(95)<600'],
      'http_req_duration{endpoint:users}': ['p(95)<300'],
      'http_req_failed{endpoint:products}': ['rate<0.02'],
      'http_req_failed{endpoint:orders}': ['rate<0.03'],
      'http_req_failed{endpoint:users}': ['rate<0.01'],
    },
  };

  export default function () {
    // Product endpoints
    http.batch([
      ['GET', `${BASE_URL}/products`, null, { tags: { endpoint: 'products' } }],
      ['GET', `${BASE_URL}/products/1`, null, { tags: { endpoint: 'products' } }],
    ]);

    // Order endpoints
    http.batch([
      ['GET', `${BASE_URL}/orders`, null, { tags: { endpoint: 'orders' } }],
      ['GET', `${BASE_URL}/orders/1`, null, { tags: { endpoint: 'orders' } }],
    ]);

    // User endpoints
    http.batch([
      ['GET', `${BASE_URL}/users`, null, { tags: { endpoint: 'users' } }],
      ['GET', `${BASE_URL}/users/1`, null, { tags: { endpoint: 'users' } }],
    ]);

    sleep(2);
  }
}