Suite test yang hijau terasa meyakinkan — sampai kita bertanya: bagian kode mana yang sebenarnya pernah dijalankan oleh test? Test yang hanya menyentuh jalur sukses akan tetap hijau meskipun seluruh cabang penanganan error tidak pernah diperiksa. Test coverage adalah pengukuran untuk kekosongan itu: metrik yang menunjukkan persentase baris, cabang, dan function yang benar-benar tereksekusi saat suite berjalan. Pada Chapter ini kita menjalankan laporan coverage bawaan Node.js, membaca kolom-kolomnya, menaikkan cakupan dengan test tambahan, dan memasang ambang batas agar angka tersebut dijaga otomatis oleh pipeline CI.
Menjalankan Laporan Coverage #
Node.js membawa penghitung coverage tanpa package tambahan: flag --experimental-test-coverage pada node --test. Sebagai bahan ukur, kita memakai module kecil dengan dua function — diskon yang memiliki cabang validasi, dan pajak yang lurus — beserta test yang sengaja hanya menguji satu jalur:
export function diskon(harga, persen) {
if (persen < 0 || persen > 100) {
throw new Error('persen harus di antara 0 dan 100');
}
return Math.round(harga * (1 - persen / 100));
}
export function pajak(harga, tarif) {
return Math.round(harga * (tarif / 100));
}import { test } from 'node:test';
import assert from 'node:assert';
import { diskon } from './perhitungan.mjs';
test('menghitung diskon yang sah', () => {
assert.strictEqual(diskon(200000, 25), 150000);
});node --test --experimental-test-coverage perhitungan.test.mjsOutput yang Dihasilkan:
✔ menghitung diskon yang sah
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ ----------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ ----------------------------------------------------------------
ℹ perhitungan.mjs | 60.00 | 66.67 | 50.00 | 3-4 9-10
ℹ ----------------------------------------------------------------
ℹ all files | 60.00 | 66.67 | 50.00 |
ℹ ----------------------------------------------------------------Setelah rangkuman test yang sudah kita kenal, muncul blok laporan coverage berbentuk tabel. Angka-angkanya bersumber dari V8 — mesin JavaScript yang menandai baris mana yang tereksekusi selama proses berjalan — sehingga tidak ada instrumentasi manual yang perlu kita pasang. Pada terminal pembaca, tabel tersebut dibungkus baris pembuka dan penutup laporan; sesuai konvensi Section ini, output pada buku hanya menampilkan isi tabelnya.
Membaca Kolom Laporan #
| Kolom | Yang diukur | Pada laporan di atas |
|---|---|---|
line % | Persentase baris yang tereksekusi setidaknya sekali | 60.00 — dari 10 baris kode, 6 baris pernah berjalan |
branch % | Persentase cabang keputusan (if, ` | |
funcs % | Persentase function yang pernah dipanggil | 50.00 — hanya diskon; pajak belum pernah |
uncovered lines | Daftar baris yang tidak pernah tereksekusi | 3-4 9-10 |
Kolom uncovered lines adalah pintu masuk paling praktis: baris 3-4 adalah jalur penolakan throw di dalam diskon, sedangkan baris 9-10 adalah seluruh isi pajak. Dua baris pertama menunjukkan penyakit yang sering luput — test kita hanya memperlihatkan hari baik. Cabang penanganan error justru merupakan tempat kesalahan paling sering bersarang, karena jarang dijalankan manual saat mencoba aplikasi.
Menaikkan Cakupan #
Membaca laporan di atas, pekerjaan kita jelas: tambahkan test untuk persen negatif, persen di atas seratus, dan function pajak. Berkas kedua melakukan persis itu:
import { test } from 'node:test';
import assert from 'node:assert';
import { diskon, pajak } from './perhitungan.mjs';
test('menghitung diskon yang sah', () => {
assert.strictEqual(diskon(200000, 25), 150000);
});
test('diskon negatif ditolak', () => {
assert.throws(() => diskon(200000, -10), {
message: 'persen harus di antara 0 dan 100',
});
});
test('diskon di atas seratus ditolak', () => {
assert.throws(() => diskon(200000, 150), {
message: 'persen harus di antara 0 dan 100',
});
});
test('menghitung pajak dari harga', () => {
assert.strictEqual(pajak(150000, 11), 16500);
});node --test --experimental-test-coverage perhitungan-lengkap.test.mjsOutput yang Dihasilkan:
✔ menghitung diskon yang sah
✔ diskon negatif ditolak
✔ diskon di atas seratus ditolak
✔ menghitung pajak dari harga
ℹ tests 4
ℹ suites 0
ℹ pass 4
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ ----------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ ----------------------------------------------------------------
ℹ perhitungan.mjs | 100.00 | 100.00 | 100.00 |
ℹ ----------------------------------------------------------------
ℹ all files | 100.00 | 100.00 | 100.00 |
ℹ ----------------------------------------------------------------Seluruh kolom kini 100.00 dan daftar uncovered lines kosong. Perhatikan bahwa dua test penolakan sekaligus menutup kolom branch %: kondisi persen < 0 dibuktikan lewat -10, kondisi persen > 100 lewat 150 — masing-masing sisi operator || mendapat gilirannya. Proses yang barusan kita lakukan — jalankan coverage, baca baris kosong, tulis test untuk menutupnya, ulangi — adalah daur kerja standar saat menumbuhkan suite test pada kode warisan yang belum teruji.
Ambang Batas untuk Pipeline CI #
Angka coverage hanya berguna jika dijaga. Flag --test-coverage-lines=80 menuntut paling sedikit 80 persen cakupan baris; jika tidak tercapai, runner keluar dengan kode gagal. Mari kita padukan flag tersebut dengan berkas test parsial yang masih 60 persen:
node --test --experimental-test-coverage --test-coverage-lines=80 perhitungan.test.mjs
echo "kode keluar: $?"Output yang Dihasilkan:
✔ menghitung diskon yang sah
ℹ tests 1
ℹ suites 0
ℹ pass 1
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ Error: 60.00% line coverage does not meet threshold of 80%.
ℹ ----------------------------------------------------------------
ℹ file | line % | branch % | funcs % | uncovered lines
ℹ ----------------------------------------------------------------
ℹ perhitungan.mjs | 60.00 | 66.67 | 50.00 | 3-4 9-10
ℹ ----------------------------------------------------------------
ℹ all files | 60.00 | 66.67 | 50.00 |
ℹ ----------------------------------------------------------------
kode keluar: 1Laporan tetap dicetak lengkap, diawali baris Error: 60.00% line coverage does not meet threshold of 80%., dan proses keluar dengan kode 1 — pipeline CI yang memeriksa kode keluar akan menghentikan build. Selain --test-coverage-lines, tersedia pasangannya untuk cabang (--test-coverage-branches) dan function (--test-coverage-functions), semuanya didokumentasikan pada halaman CLI Node.js
. Ambang yang sehat biasanya tidak dari seratus persen: nilai 80-90 persen memberi ruang bagi kode penanganan khusus yang memang sulit diuji, sambil tetap menangkap kekosongan yang tidak disengaja.
Coverage Bukan Jaminan Mutu #
Angka seratus persen mudah dicapai dengan cara yang salah. Test berikut menaikkan cakupan pajak ke 100 persen tanpa memeriksa apa pun:
import { test } from 'node:test';
import { pajak } from './perhitungan.mjs';
test('memanggil pajak demi angka coverage', () => {
pajak(150000, 11); // hasil dibuang, tidak ada assert
});Blok di atas hanya ilustrasi — jangan ditiru. Function dipanggil sehingga seluruh barisnya terhitung tereksekusi, tetapi jika pajak keliru menghitung, tidak ada satu pun baris test yang protes. Coverage mengukur kode pernah dijalankan, bukan kode pernah diperiksa; kualitas assertion tidak masuk ke dalam metrik.
Ke Mana Setelah Ini #
Kita kini bisa mengukur seberapa jauh test menyentuh kode, menutup celah berdasarkan daftar uncovered lines, dan menjaga angkanya lewat ambang batas. Seluruh peralatan yang telah kita bangun — assertion, async test, mock, coverage — siap diangkat ke ukuran berikutnya: memastikan seluruh komponen bekerja benar saat disatukan. Chapter Integration Test untuk HTTP Server menyusun server sungguhan di dalam test dan mengujinya lewat fetch seperti halnya client.