Kodeloka
  1. Kodeloka
  2. Kelas k6
  3. Materi

Modul 11 · Modul 11 dari 15

Di Luar HTTP: WebSocket, gRPC, dan Browser Testing

Menguji protokol selain HTTP dan kapan Anda benar-benar membutuhkan pengujian level browser.

WebSocket

Untuk aplikasi realtime: chat, notifikasi, dashboard live, trading. k6 menyediakan modul WebSocket berbasis event.

⚠️ Jalur import berubah di k6 v1.0

Sejumlah modul k6/experimental/* sudah lulus menjadi modul stabil. Skrip lama yang mengimpor k6/experimental/websockets dan k6/experimental/grpc perlu diperbarui menjadi k6/net/websockets dan k6/net/grpc. Modul k6/ws generasi pertama (berbasis callback, satu koneksi memblokir satu VU) sudah usang — gunakan modul berbasis event di bawah ini yang bisa menangani banyak koneksi paralel per VU.

websocket.jsjavascript
import { WebSocket } from 'k6/net/websockets';
import { check, sleep } from 'k6';
import { Trend, Counter } from 'k6/metrics';

const latensiPesan = new Trend('latensi_pesan_ws', true);
const pesanDiterima = new Counter('pesan_ws_diterima');

export default function () {
  const ws = new WebSocket('wss://echo.websocket.org');
  const dikirimPada = {};

  ws.onopen = function () {
    // Kirim pesan berkala, seperti klien chat sungguhan
    let n = 0;
    const timer = setInterval(function () {
      if (n++ >= 10) { clearInterval(timer); ws.close(); return; }
      const id = 'msg-' + n;
      dikirimPada[id] = Date.now();
      ws.send(JSON.stringify({ id: id, teks: 'halo dunia' }));
    }, 1000);
  };

  ws.onmessage = function (e) {
    pesanDiterima.add(1);
    try {
      const data = JSON.parse(e.data);
      if (dikirimPada[data.id]) {
        latensiPesan.add(Date.now() - dikirimPada[data.id]);
      }
    } catch (err) { /* pesan bukan JSON, abaikan */ }
  };

  ws.onerror = function (e) { console.error('WS error: ' + e.error); };
}
⚠️ WebSocket = koneksi yang tetap terbuka

Berbeda dengan HTTP, satu VU WebSocket menahan satu koneksi TCP selama hidupnya. 10.000 VU berarti 10.000 koneksi terbuka bersamaan — Anda akan menabrak batas file descriptor OS (ulimit -n, default sering hanya 1024) jauh sebelum menabrak batas server.

Naikkan dengan ulimit -n 65535 di mesin generator sebelum menjalankan tes.

gRPC

grpc.jsjavascript
import grpc from 'k6/net/grpc';
import { check } from 'k6';

const client = new grpc.Client();
// WAJIB di init context — pemuatan .proto tidak boleh di dalam default()
client.load(['definisi'], 'layanan.proto');

export default function () {
  client.connect('grpc.contoh.id:9000', {
    plaintext: false,     // true bila tanpa TLS (hanya untuk lokal)
    timeout: '10s',
  });

  const res = client.invoke('paket.LayananProduk/AmbilProduk', { id: 123 });

  check(res, {
    'status OK':  (r) => r.status === grpc.StatusOK,
    'ada nama':   (r) => r.message.nama !== undefined,
  });

  client.close();   // JANGAN lupa, kalau tidak koneksi bocor
}

Browser Testing dengan k6/browser

Menjalankan Chromium sungguhan untuk mengukur pengalaman pengguna di sisi frontend: waktu render, interaksi, dan Core Web Vitals.

browser.jsjavascript
import { browser } from 'k6/browser';
import { check } from 'k6';

export const options = {
  scenarios: {
    ui: {
      executor: 'shared-iterations',
      vus: 1,
      iterations: 5,
      options: { browser: { type: 'chromium' } },
    },
  },
  thresholds: {
    browser_web_vital_lcp: ['p(95)<2500'],   // Largest Contentful Paint
    browser_web_vital_cls: ['p(95)<0.1'],    // Cumulative Layout Shift
    browser_web_vital_inp: ['p(95)<200'],    // Interaction to Next Paint
  },
};

export default async function () {
  const page = await browser.newPage();
  try {
    await page.goto('https://contoh.id/produk', { waitUntil: 'networkidle' });

    await page.locator('input[name="cari"]').type('sepatu');
    await Promise.all([
      page.waitForNavigation(),
      page.locator('button[type="submit"]').click(),
    ]);

    const jumlah = await page.locator('.kartu-produk').count();
    check(jumlah, { 'ada hasil pencarian': (n) => n > 0 });

    await page.screenshot({ path: 'hasil.png' });
  } finally {
    await page.close();   // WAJIB, kalau tidak proses Chromium menumpuk
  }
}
⛔ Browser testing SANGAT mahal — jangan dipakai untuk beban

Satu instance Chromium memakan ± 100–300 MB RAM dan satu inti CPU. Menjalankan 100 VU browser butuh mesin dengan puluhan GB RAM dan puluhan core — biaya yang tidak masuk akal.

Pola yang benar (hybrid): gunakan protokol HTTP untuk menghasilkan beban besar (ribuan VU), dan jalankan 1–5 VU browser secara paralel hanya untuk mengukur pengalaman frontend di bawah beban tersebut.

Pola Hybrid: Beban Protokol + Pengukuran Browser

hybrid.jsjavascript
export const options = {
  scenarios: {
    beban_protokol: {
      executor: 'constant-arrival-rate',
      rate: 200, timeUnit: '1s', duration: '10m',
      preAllocatedVUs: 300, maxVUs: 600,
      exec: 'bebanApi',
    },
    ukur_frontend: {
      executor: 'constant-vus',
      vus: 2,                       // cukup 2 — ini pengukur, bukan penghasil beban
      duration: '10m',
      options: { browser: { type: 'chromium' } },
      exec: 'ukurUi',
    },
  },
};