Memecah skrip load test menjadi beberapa file dengan ECMAScript modules, membangun fungsi helper untuk HTTP request dan autentikasi, serta merancang fungsi skenario yang bisa dipakai ulang agar test tetap terawat saat tumbuh besar.

Di episode 5 kalian menguasai teknik yang membuat skrip menjadi hidup: token diekstrak dari respons, sesi disimpan per VU, request paralel lewat http.batch(). Tapi ada efek samping yang tidak bisa dihindari: file skrip semakin panjang. Kode login ditulis ulang di setiap skenario, header Authorization diulang di mana-mana, dan satu perubahan kecil — misalnya URL endpoint berubah — memaksa kalian menyunting banyak tempat.
Episode ini mengajarkan kebiasaan yang membedakan skrip sekali pakai dari aset engineering yang sesungguhnya: modularisasi. Kita akan memecah skrip menjadi beberapa file dengan ECMAScript modules (import/export), membangun reusable function untuk request dan autentikasi, dan merancang skenario yang bisa dipakai ulang lintas test. Analoginya: di episode 5 kalian belajar memasak satu menu; episode 6 mengajarkan menulis buku resep yang bisa dipakai dapur mana pun.
Sebuah load test yang tumbuh tanpa struktur adalah bom waktu. Bayangkan tim kalian punya sepuluh skrip test, masing-masing berisi kode login yang disalin-tempel. Lalu tim auth mengubah format respons login — sekarang sepuluh file harus diperbaiki, dan kemungkinan besar ada yang terlewat. Modularisasi memecahkan tiga masalah sekaligus:
main.js yang pendek dan jelas jauh lebih mudah di-review daripada satu file 300 baris. Kode yang mudah di-review adalah kode yang bisa di-deploy dengan percaya diri.Sebelum menulis, pahami batasan modul di k6 (ingat pelajaran episode 2: k6 bukan Node.js):
import { getJson } from "./utils/http-client.js", bukan "./utils/http-client".k6/*, modul lokal, dan library dari jslib (kita pakai di episode 8).Struktur folder yang akan kita bangun:
learn-k6/
├── main.js
├── auth.js
├── config.js
└── utils/
└── http-client.jsFile ini adalah wajah HTTP dari skrip kalian. Alih-alih menulis http.get dengan header Authorization di setiap skenario, kita bungkus dalam fungsi yang bernama jelas:
import http from "k6/http";
export function getJson(url, token) {
return http.get(url, {
headers: {
Accept: "application/json",
Authorization: `Bearer ${token}`,
},
});
}
export function postJson(url, body, token) {
const headers = { "Content-Type": "application/json" };
if (token) {
headers.Authorization = `Bearer ${token}`;
}
return http.post(url, JSON.stringify(body), { headers });
}Kenapa pola ini bernilai? Perhatikan dua hal. Pertama, postJson menangani detail yang mudah terlupa: JSON.stringify untuk body dan header Content-Type. Kedua, token bersifat opsional — request login (belum punya token) dan request terautentikasi bisa memakai fungsi yang sama tanpa duplikasi. Jika suatu hari tim kalian pindah dari header Authorization ke API key, cukup satu file yang berubah, bukan puluhan skenario.
Login adalah logika paling sering diulang dan paling mudah salah. Kita bungkus lengkap dengan pola state per VU dari episode 5, sehingga pemanggil cukup menulis satu baris:
import http from "k6/http";
const sessions = {};
export function login(baseUrl, credentials) {
if (sessions[__VU]) {
return sessions[__VU];
}
const res = http.post(`${baseUrl}/login`, JSON.stringify(credentials), {
headers: { "Content-Type": "application/json" },
});
const token = res.json("access_token");
sessions[__VU] = token;
return token;
}Perhatikan tiga keputusan desain di balik fungsi ini:
sessions yang dikunci __VU — persis pola episode 5, tapi kini dibungkus jadi fungsi. Iterasi kedua hingga seterusnya tidak lagi memanggil /login.Satu catatan: dalam produksi, pertimbangkan token yang kadaluarsa — jika API kalian memakai JWT berumur pendek, tambahkan logika refresh atau hapus cache saat respons 401. Untuk test pendek, pola di atas sudah tepat.
Konfigurasi load test juga layak jadi modul sendiri. Dengan fungsi pembangun options, kalian bisa membuat varian test dari satu sumber aturan:
export function buildOptions({ vus, duration }) {
return {
vus,
duration,
thresholds: {
http_req_duration: ["p(95)<500"],
http_req_failed: ["rate<0.01"],
},
};
}Threshold disatukan di satu tempat, sehingga setiap skenario yang memakai buildOptions otomatis mendapat standar kualitas yang sama. Varian beban tinggal disuntikkan lewat argumen — pola ini akan bertemu lagi di episode 7 saat kita menghubungkan konfigurasi dengan environment variables.
Satu load test jarang hanya satu request. Pola terbaik adalah menulis perjalanan pengguna (journey) sebagai fungsi yang menerima konteks — di sini baseUrl dan token — lalu memanggil helper:
import { check, group } from "k6";
import { getJson } from "./utils/http-client.js";
export function userJourney(baseUrl, token) {
group("dashboard", () => {
const res = getJson(`${baseUrl}/dashboard`, token);
check(res, { "dashboard 200": (r) => r.status === 200 });
});
}Fungsi ini tidak peduli skenario mana yang memakainya. Ia bisa dipanggil dari skrip smoke, skrip load, atau skrip soak — dengan parameter yang berbeda — tanpa ditulis ulang. Inilah inti reusable scenario function: memisahkan apa yang diuji (journey) dari bagaimana bebannya diatur (options dan executor).
Semua modul disatukan dalam satu file utama yang pendek dan ekspresif:
import { sleep } from "k6";
import { getJson } from "./utils/http-client.js";
import { login } from "./auth.js";
import { buildOptions } from "./config.js";
import { userJourney } from "./journeys.js";
export const options = buildOptions({ vus: 10, duration: "30s" });
const BASE_URL = "https://api.example.com";
const credentials = {
email: "user@example.com",
password: "rahasia123",
};
export default function () {
const token = login(BASE_URL, credentials);
userJourney(BASE_URL, token);
sleep(1);
}Bandingkan dengan satu file 300 baris: main.js ini bisa dipahami dalam tiga puluh detik. Alur eksekusinya jelas — ambil token, jalankan journey, istirahat sebentar. Detail teknis tersembunyi di balik fungsi yang bernama. Itulah tanda arsitektur yang sehat.
Fungsi lifecycle lain juga bisa dimodularisasi: setup dan teardown dapat diimpor dari modul terpisah lalu di-export ulang dari main.js dengan pola export { prepareTestData as setup } — menjaga fase persiapan dan pembersihan tetap terpisah dari beban utama.
Note
Disiplin kunci modularisasi: satu file, satu tanggung jawab. Helper HTTP tidak boleh berisi logika login, auth tidak boleh berisi logika konfigurasi, dan main.js tidak boleh berisi detail request. Jika kalian merasa sebuah file mulai "memikirkan" dua hal sekaligus, saatnya dipecah.
.js di import lokal. k6 menolak path tanpa ekstensi dengan error resolusi modul — gejala yang paling sering membingungkan pemula.loginArifAtStaging() tidak bisa dipakai ulang; login(baseUrl, credentials) bisa.__VU harus tinggal di tempat penyimpanannya.Di episode 6 ini kalian telah merapikan skrip k6 menjadi proyek yang terstruktur:
.js dan eksekusi di init context.getJson dan postJson membungkus detail HTTP dan autentikasi.login() dengan cache per VU, siap dipakai lintas skenario.buildOptions() menyatukan threshold di satu sumber.Jalankan hasilnya dengan k6 run main.js dan perhatikan bahwa satu folder kecil ini bisa menghasilkan banyak varian test hanya dengan mengubah argumen. Ada satu tautan yang belum dihubungkan: nilai vus, duration, dan URL masih hardcode. Di episode 7 kita membuka itu — Konfigurasi Load Test dan Environment Variables: merancang stages ramping, menegakkan threshold lanjutan, memakai __ENV untuk parameter runtime, dan menjalankan varian test yang berbeda untuk local, staging, dan lingkungan menyerupai produksi. Sampai jumpa di episode 7!