Lompat ke konten
Kembali ke modul

Testing, Debugging, dan Kualitas Kode · 2/7

Unit test dengan Vitest

describe, it, expect — dan cara memilih kasus yang benar-benar bisa menangkap kesalahan.

Baca 30 menit

Setelah pelajaran ini kamu bisa

  • Menulis test dengan describe, it, dan expect
  • Memilih matcher yang memberi pesan kegagalan paling jelas
  • Memilih kasus test yang benar-benar bisa menangkap kesalahan
  • Menguji kode asinkron dan error yang diharapkan

Framework test itu jauh lebih kecil dari kelihatannya. Tiga function, sebuah daftar, dan sebuah perbandingan. Membuatnya butuh sekitar tiga puluh baris, dan setelahnya tidak ada lagi yang terasa misterius soal testing.

Coba sendiri

Framework test yang berfungsi, lalu test sungguhan dijalankan lewatnya. Ini benar-benar cara kerja vitest di dalamnya.

Hasil

Tekan Jalankan untuk melihat hasilnya.

Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.

Itu seluruh idenya. it menangkap error yang dilempar lalu mencatat kegagalan; expect melempar saat perbandingannya gagal. Setiap framework test yang akan kamu pakai adalah ini, plus pelaporan, pemantauan, dan paralelisme.

Yang sungguhan

ts
import { describe, expect, it } from "vitest";
import { interpolate } from "./interpolate";

describe("interpolate", () => {
  it("substitutes a single placeholder", () => {
    expect(interpolate("{count} modules", { count: 12 })).toBe("12 modules");
  });

  it("leaves unknown placeholders verbatim so the gap is visible", () => {
    expect(interpolate("{count} modules", {})).toBe("{count} modules");
  });

  it("does not treat a zero value as missing", () => {
    expect(interpolate("{done} done", { done: 0 })).toBe("0 done");
  });
});
Ini features/i18n/interpolate.test.ts dari proyek ini, tanpa diedit. Perhatikan nama test-nya: masing-masing menyatakan sebuah aturan, jadi kegagalannya terbaca sebagai "aturan ini rusak".
MatcherUntuk
toBe(x)Primitive dan identitas. Memakai Object.is.
toEqual(x)Perbandingan struktural mendalam untuk object dan array
toThrow(msg)Function yang harus melempar error. Kirim function-nya, bukan pemanggilannya.
toHaveLength(n)Kegagalannya lebih jelas daripada .length dengan toBe
toContain(x)Sebuah item array atau substring
toBeDefined()Bukan undefined — lebih baik daripada toBe(undefined) yang dinegasikan
await expect(p).resolves.toBe(x)Nilai hasil resolve sebuah promise
await expect(p).rejects.toThrow()Promise yang harus reject

Memilih kasus

Satu test per function bukan sebuah suite. Kasus yang menemukan bug adalah kasus di batas dan kasus yang mengejutkan.

  • Kasus normal. Satu sudah cukup.
  • Kosong. String kosong, array kosong, nol, tanpa row.
  • Batas. Tepat di batasnya, dan satu di masing-masing sisinya.
  • Type yang salah. null, undefined, angka di tempat yang seharusnya string.
  • Jebakan falsy. 0 dan "" adalah dua yang merusak fallback ||.
  • Bug yang baru kamu perbaiki. Selalu. Itulah yang mencegahnya kembali.

Async dan error

ts
// A resolved value.
it("hashes a password", async () => {
  const hash = await hashPassword("secret123");
  expect(hash).not.toContain("secret123");
});

// A rejection, without a try/catch in the test.
it("returns false for a malformed stored hash instead of throwing", async () => {
  await expect(verifyPassword("anything", "not-a-hash")).resolves.toBe(false);
});

// Table-driven: one assertion, many inputs. Note the label in expect(),
// which makes the failure say WHICH input broke.
it("rejects every malformed hash shape", async () => {
  for (const stored of ["", "not-a-hash", "scrypt$16384$8$1$aa", "$$$$$"]) {
    await expect(verifyPassword("x", stored), stored).resolves.toBe(false);
  }
});
Argument kedua pada expect adalah label. Di dalam loop, itulah pembeda antara "salah satu dari lima input gagal" dan mengetahui yang mana.

Tugas praktik

Tulis features/lesson/slugify.ts dengan function slugify dari runner-nya, dan slugify.test.ts di sebelahnya yang mencakup setiap kasus di daftar periksa di atas. Jalankan bun run test slugify. Lalu sengaja rusak function-nya dan pastikan pesan kegagalannya mengidentifikasi aturan mana yang rusak.

Hasil yang diharapkan

Enam atau tujuh test lolos, dan kegagalan yang jelas saat kamu merusaknya.