Membongkar cara kerja k6 di balik layar: engine Go dengan goja, model eksekusi virtual users dan iterasi, lifecycle test dari init hingga teardown, serta komponen inti seperti modul http, checks, thresholds, metrics, dan opsi eksekusi seperti stages.

Di episode 1 kalian memahami mengapa k6 lahir: menjawab keterbatasan JMeter, Gatling, dan Locust lewat scripting JavaScript, integrasi CI, dan efisiensi engine Go. Sekarang saatnya membuka kap mesin. Episode 2 membedah konsep dasar dan arsitektur utama k6 — karena skrip k6 yang bagus lahir dari pemahaman bagaimana tool ini mengeksekusi skrip, bukan dari menghafal sintaks.
Ada tiga pertanyaan besar yang akan kita jawab: apa yang terjadi saat k6 run dijalankan?, bagaimana k6 mengelola ribuan virtual users dalam satu proses?, dan di mana letak setiap komponen seperti checks, thresholds, dan metrics dalam alur itu?. Pahami bab ini, dan episode 3 nanti menulis skrip pertama akan terasa seperti mengisi bagian-bagian dari peta yang sudah kalian pegang.
Pemahaman pertama yang wajib kalian pegang: k6 TIDAK berjalan di Node.js. Meskipun script ditulis dalam JavaScript, eksekusinya ditangani oleh goja — mesin ECMAScript (JavaScript) yang ditulis murni dalam bahasa Go dan tertanam di dalam binary k6.
Apa konsekuensinya bagi kalian sebagai penulis skrip?
require() dari npm, tidak ada fs, path, process, atau http milik Node. Import hanya diperbolehkan untuk modul yang disediakan k6 (k6, k6/http, k6/ws, dan lain-lain) serta file lokal milik kalian sendiri.const, let, arrow function, template literal, destructuring — semua jalan. Goja terus diperbarui mengikuti standar ECMAScript modern.Analoginya: skrip JavaScript di k6 seperti resep yang dijalankan oleh dapur berbahan bakar Go, bukan dapur Node.js. Resepnya (JavaScript) sama-sama dibaca, tapi kompor, panci, dan api yang dipakai berbeda — sehingga beberapa bahan (library npm) tidak tersedia.
Konsep sentral k6 adalah Virtual User (VU). Bayangkan setiap VU sebagai satu pengguna simulasi: ia menjalankan fungsi utama skrip secara berulang-ulang, satu iterasi per satu, dalam loop. Model eksekusinya:
k6 run meluncurkan sejumlah VU (ditentukan oleh vus).Model ini krusial dipahami karena berimplikasi pada skrip: variabel global di modul di-share antar semua VU, sedangkan variabel di dalam fungsi default tidak di-share antar iterasi. Inilah yang membuat data-driven testing (episode 9) dan session handling (episode 6) menjadi bab tersendiri — state antar iterasi tidak otomatis saling menempel.
import http from "k6/http";
export default function () {
// Satu iterasi = satu pemanggilan fungsi ini
http.get("https://test.k6.io");
}Jika k6 dijalankan dengan vus: 10 dan duration: "30s", maka 10 VU masing-masing memanggil fungsi di atas berulang kali selama 30 detik — total iterasi bergantung pada seberapa cepat tiap request selesai. Inilah bentuk paling dasar dari "load".
Eksekusi k6 mengikuti alur lifecycle yang terdiri dari empat fase. Memahami fase ini menentukan di mana sebuah kode harus diletakkan:
| Fase | Kapan Dieksekusi | Contoh Penggunaan |
|---|---|---|
| init | Sekali, saat skrip di-load, sebelum VU lahir | Import modul, load file, setup data global |
| setup | Sekali, sebelum test dimulai | Menyiapkan token yang dipakai semua VU |
| default (VU code) | Berulang, satu kali per iterasi per VU | Request HTTP utama yang diuji |
| teardown | Sekali, setelah semua iterasi selesai | Membersihkan resource, laporan akhir |
import http from "k6/http";
export function setup() {
// Fase setup: persiapan sekali jalan
const res = http.post("https://api.example.com/login");
return { token: res.json().token };
}
export function teardown(data) {
// Fase teardown: pembersihan
console.log("Selesai, token yang dipakai:", data.token);
}
export default function (data) {
// Fase default: beban utama yang diulang
http.get("https://api.example.com/profile");
}Pola di atas menjelaskan kenapa lifecycle penting: token hasil login cukup disiapkan sekali di setup, lalu diwariskan ke semua VU — alih-alih setiap VU login sendiri-sendiri di setiap iterasi (yang justru mengacaukan hasil pengukuran endpoint login).
Modul k6/http adalah perpustakaan untuk mengirim request HTTP: http.get, http.post, http.put, http.request, hingga http.batch untuk mengirim banyak request sekaligus. Seluruh pergerakan beban kalian akan melalui modul ini.
Check adalah assertion fungsional: pernyataan benar-salah tentang hasil sebuah request. Misalnya "status harus 200" atau "body mengandung kata 'sukses'". Berbeda dengan throw error, check yang gagal tidak menghentikan iterasi — ia hanya dicatat sebagai kegagalan. Ini membedakan validasi data (checks) dari kegagalan teknis fatal.
import { check } from "k6";
import http from "k6/http";
const res = http.get("https://test.k6.io");
check(res, {
"status is 200": (r) => r.status === 200,
});Threshold adalah pass/fail criteria untuk metrik — ia menghentikan evaluasi pengujian berdasarkan ambang batas. Contoh paling umum: http_req_duration harus memiliki p(95) di bawah 500 milidetik. Jika threshold dilanggar saat test selesai, k6 keluar dengan exit code 99 — sinyal yang bisa dipakai CI untuk menggagalkan pipeline.
Setiap request yang berjalan otomatis menghasilkan metrik: http_req_duration (durasi request), http_req_failed (rasio error), http_reqs (jumlah request per detik), http_req_waiting (waktu tunggu), dan lain-lain. Metrik ini bisa dianalisis lewat summary di terminal, atau disalurkan ke sistem observability eksternal (episode 15).
Skrip k6 mengatur perilaku load lewat export const options. Empat opsi paling mendasar yang akan kalian temui terus-menerus:
export const options = {
vus: 10,
duration: "30s",
thresholds: {
http_req_duration: ["p(95)<500"],
},
};vus — jumlah virtual users yang berjalan bersamaan.duration — berapa lama test berjalan.stages — rencana ramping: menaikkan dan menurunkan beban secara bertahap seiring waktu, untuk mensimulasikan lonjakan traffic yang realistis.thresholds — batas kelulusan yang digagalkan lewat exit code 99.export const options = {
stages: [
{ duration: "1m", target: 50 }, // ramp up ke 50 VU
{ duration: "3m", target: 50 }, // stabil di 50 VU
{ duration: "1m", target: 0 }, // ramp down ke 0
],
};Stages adalah perbedaan paling nyata antara beban statis dan beban realistis: aplikasi produksi jarang dihantam 500 user yang tiba-tiba muncul sekaligus, melainkan naik bertahap. Episode 8 akan membedah konfigurasi ini lebih dalam.
Di luar komponen inti, k6 membuka tiga pintu ekstensi yang akan dipakai berulang di series ini:
tps_transaksi) lewat k6/metrics untuk mengukur hal yang spesifik bagi aplikasi kalian, bukan hanya metrik HTTP bawaan.{ method: "POST", endpoint: "/login" }) sehingga hasil bisa difilter dan dikelompokkan di dashboard.__ENV untuk memparameterisasi skrip tanpa mengubah kode: k6 run script.js -e BASE_URL=https://staging.example.com.Ekstensi xk6 (episode 19) bahkan memungkinkan kalian mengkompilasi binary k6 kustom dengan modul tambahan — dari protokol baru sampai integrasi ke sistem internal.
Warning
Satu kesalahan konsep yang paling sering terjadi: menganggap check yang gagal akan menghentikan test. Check hanya mencatat. Yang menghentikan test secara formal hanyalah threshold (dengan exit code 99) atau error fatal. Desain check dan threshold yang jelas sejak awal akan menyelamatkan kalian dari kebingungan di episode 4.
Di episode 2 ini kalian telah membongkar arsitektur k6:
http, check (pencatat), thresholds (penilai kelulusan), dan metrics (data terukur).vus, duration, stages, thresholds, hingga custom metrics, tags, dan environment variables.Kalian sekarang memiliki peta mental lengkap. Di episode 3 kita langsung praktek: menulis skrip k6 pertama — struktur dasar script dengan import, options, export default function, dan check, menjalankan HTTP GET dan POST, memvalidasi respons, memakai group() untuk memecah skenario, serta melihat output k6 run script.js secara nyata. Sampai jumpa di episode 3!