Belajar Jest - Menulis Test Unit
Episode 4 of 23

Belajar Jest - Menulis Test Unit

Episode inti ini mengajarkan struktur test unit: deklarasi dengan test dan describe, matchers dasar seperti toBe, toEqual, toContain, dan toBeTruthy, siklus setup teardown dengan beforeEach dan kawan-kawan, serta cara menguji pure functions dan edge cases.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Pendahuluan

Inilah episode yang paling sering dipraktikkan dalam karir testing: menulis test unit. Episode 4 mengajarkan cara menyusun test yang jelas, memakai matchers dengan tepat, mengatur siklus hidup setup dan teardown, serta menguji pure functions sampai edge cases-nya.

Sebuah test unit yang baik bukan sekadar "kode yang lolos". Dia adalah dokumen hidup: membaca namanya harus menjelaskan perilaku yang dijamin, dan gagalnya harus langsung menunjuk ke bagian kode yang bermasalah. Mari bangun kebiasaan itu mulai sekarang.

Deklarasi Test dengan test dan describe

Struktur Dasar

Dua fungsi inti untuk menyusun test: test (atau aliasnya it) untuk mendeklarasikan satu test, dan describe untuk mengelompokkan test yang berkaitan:

JStest dan describe
describe("fungsi tambah", () => {
  test("menjumlahkan dua angka positif", () => {
    expect(1 + 2).toBe(3);
  });
 
  test("menjumlahkan angka nol", () => {
    expect(5 + 0).toBe(5);
  });
});

describe("fungsi tambah", () => {}) membuat blok pengelompok, dan test("...", () => {}) mendeklarasikan satu test dengan nama yang deskriptif. Nama test adalah kontrak perilaku — tulis seolah-olah dibaca orang lain.

Pola Nama yang Deskriptif

Hindari nama seperti "test satu" atau "test dua". Gunakan pola yang menjelaskan kondisi dan hasil: "menjumlahkan dua angka positif", "mengembalikan error saat argumen kosong". Nama yang baik membuat output --verbose terasa seperti daftar spesifikasi.

Matchers Dasar

toBe dan toEqual

Matcher toBe memakai Object.is untuk membandingkan nilai primitif, sedangkan toEqual membandingkan struktur secara rekursif. Perbedaan ini penting:

JSPerbedaan toBe dan toEqual
test("toBe untuk primitif", () => {
  expect(2 + 2).toBe(4);
  expect("hello").toBe("hello");
});
 
test("toEqual untuk objek", () => {
  const data = { nama: "arif", umur: 30 };
  expect(data).toEqual({ nama: "arif", umur: 30 });
});
 
test("toBe gagal untuk objek", () => {
  expect({ a: 1 }).not.toBe({ a: 1 });
});

expect(data).toEqual({ nama: "arif", umur: 30 }) membandingkan isi objek, bukan referensinya. Objek dua instance yang isinya sama dianggap toEqual tetapi tidak toBe, karena toBe membandingkan referensi memori.

toContain dan toBeTruthy

Matcher lain yang sering dipakai:

  • toContain: memeriksa elemen di dalam array atau substring di dalam string.
  • toBeTruthy dan toBeFalsy: memeriksa nilai yang dianggap benar atau salah dalam konteks boolean.
  • toBeNull, toBeDefined, toBeGreaterThan, toHaveLength: matcher spesifik sesuai kebutuhan.
JSMatcher dasar lainnya
test("toContain pada array dan string", () => {
  expect(["apel", "mangga"]).toContain("mangga");
  expect("selamat datang").toContain("datang");
});
 
test("toBeTruthy dan toBeFalsy", () => {
  expect("teks").toBeTruthy();
  expect(0).toBeFalsy();
  expect(null).toBeFalsy();
});

Perhatikan expect(0).toBeFalsy() — angka nol dianggap falsy, begitu juga string kosong dan null. Memahami konversi boolean ini mencegah banyak kebingungan saat menulis assertion.

Setup dan Teardown

Empat Fungsi Siklus Hidup

Jest menyediakan empat fungsi untuk mengatur lingkungan test:

  • beforeAll: berjalan sekali sebelum semua test dalam blok.
  • beforeEach: berjalan sebelum setiap test.
  • afterEach: berjalan setelah setiap test.
  • afterAll: berjalan sekali setelah semua test selesai.
JSSiklus hidup setup teardown
describe("database test", () => {
  let daftar;
 
  beforeEach(() => {
    daftar = [];
  });
 
  afterEach(() => {
    daftar = null;
  });
 
  test("menambah satu item", () => {
    daftar.push("item");
    expect(daftar).toHaveLength(1);
  });
 
  test("dimulai kosong", () => {
    expect(daftar).toHaveLength(0);
  });
});

Karena beforeEach(() => { daftar = []; }) menjalankan ulang inisialisasi sebelum tiap test, urutan eksekusi antar test tidak saling memengaruhi — fondasi utama untuk test yang deterministik.

Testing Pure Functions dan Edge Cases

Pure Functions Paling Mudah Diuji

Pure function — fungsi yang outputnya hanya bergantung pada input dan tidak memiliki efek samping — adalah kandidat ideal untuk unit test. Setiap kombinasi input yang menarik bisa menjadi satu test:

JSPure function dan edge cases
function bagi(a, b) {
  if (b === 0) {
    throw new Error("pembagi tidak boleh nol");
  }
  return a / b;
}
 
describe("fungsi bagi", () => {
  test("pembagian normal", () => {
    expect(bagi(10, 2)).toBe(5);
  });
 
  test("hasil pecahan", () => {
    expect(bagi(1, 4)).toBeCloseTo(0.25);
  });
 
  test("pembagi nol melempar error", () => {
    expect(() => bagi(1, 0)).toThrow("pembagi tidak boleh nol");
  });
});

Ada tiga hal menarik di sini. Pertama, expect(bagi(1, 4)).toBeCloseTo(0.25) dipakai karena aritmetika float JavaScript tidak presisi untuk perbandingan toBe. Kedua, untuk menguji error, expect(() => bagi(1, 0)).toThrow(...) membungkus pemanggilan dalam fungsi — tanpa itu error akan tertangkap dan test salah gagal. Ketiga, edge case pembagi nol justru diuji karena di sanalah perilaku yang paling penting.

Penutup

Episode 4 adalah tulang punggung seluruh series: kalian sekarang bisa menyusun test dengan test dan describe, memilih matcher yang tepat antara toBe, toEqual, toContain, dan toBeTruthy, mengelola lingkungan test dengan siklus hidup setup teardown, serta menguji pure functions beserta edge cases-nya.

Inti yang harus dibawa pulang:

  • Nama test adalah kontrak perilaku — tulis yang deskriptif.
  • toBe untuk primitif, toEqual untuk struktur objek.
  • toContain untuk array dan string; pahami falsy dan truthy.
  • beforeEach dan afterEach menjaga setiap test tetap terisolasi.
  • Floating point diuji dengan toBeCloseTo, bukan toBe.
  • Error diuji dengan membungkus pemanggilan dalam fungsi dan memakai toThrow.

Di episode 5 selanjutnya kita akan masuk ke mocking dan spyingjest.fn() dan jest.spyOn(), perbedaan manual mocks dan automatic mocks, mocking modul dengan jest.mock(), serta cara mengendalikan implementasi dan perilaku reset. Inilah kunci untuk menguji kode yang bergantung pada sistem eksternal.