# Test-Driven Development: Tulis Test Terlebih Dahulu

> Kuasai siklus Red-Green-Refactor dari TDD. Contoh unit test praktis dengan Jest dan TypeScript, plus kapan TDD menguntungkan dan kapan memperlambat Anda.

URL: https://www.matthewswong.com/id/blog/test-driven-development-tdd/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2025-02-05
- Updated: 2025-02-05
- Category: Software Development

Test-Driven Development (TDD) adalah disiplin di mana Anda menulis test yang gagal sebelum menulis satu baris kode produksi. Siklus Red-Green-Refactor — tulis test yang gagal, buat berhasil dengan kode minimal, lalu refactor tanpa merusak test — terdengar kontra-intuitif pada awalnya, tetapi menghasilkan kode yang benar-benar dapat ditest by design, memiliki tingkat cacat yang jauh lebih rendah, dan jauh lebih mudah untuk di-refactor dengan aman. Postingan ini mengajarkan TDD dengan contoh TypeScript dan Jest nyata yang bisa Anda ikuti.

## Siklus Red-Green-Refactor

TDD beroperasi dalam tiga fase. Red: tulis test yang mendeskripsikan perilaku yang ingin Anda tambahkan. Test harus gagal — jika berhasil tanpa kode baru, Anda telah menulis test yang buruk atau fitur sudah ada. Green: tulis kode minimum yang diperlukan untuk membuat test berhasil — tidak lebih. Tahan godaan untuk menambahkan logika ekstra. Refactor: bersihkan kode produksi dan test dengan percaya diri, mengetahui bahwa test suite Anda akan menangkap setiap regresi.

### Contoh TDD Lengkap dalam TypeScript

Mari kita bangun shopping cart dengan TDD. Kita mulai dengan test sesederhana mungkin — bisakah kita menambahkan item dan menghitung total? — dan menumbuhkan implementasi dari sana. Setiap test mendorong perilaku baru. Perhatikan bagaimana kelas Cart akhir secara alami kecil dan terfokus: TDD mencegah over-engineering dengan memaksa Anda hanya mengimplementasikan yang ditest.

> Saat mengikuti TDD, commit setelah setiap fase Green — sebelum refactoring. Ini memberi Anda titik rollback yang bersih jika refactoring berjalan melenceng, dan menunjukkan progress inkremental TDD kepada tim Anda dalam git history.

## Testing dengan Mock dan Stub

Aplikasi nyata memiliki dependency eksternal: database, layanan email, payment gateway. TDD mengharuskan Anda menguji unit secara terisolasi, yang berarti mengganti dependency ini dengan test double. Jest's vi.mock() (atau jest.mock()) memungkinkan Anda mengganti modul dengan fake yang merekam panggilannya, memungkinkan Anda memverifikasi perilaku tanpa menyentuh infrastruktur nyata.

### Kapan Mock vs Kapan Implementasi Nyata

Mock dependency eksternal (HTTP call, layanan email, payment processor) karena lambat, tidak stabil, dan mahal dalam test. Gunakan implementasi nyata untuk logika bisnis murni dan transformasi data. Gunakan in-memory database (seperti better-sqlite3 atau test Postgres instance) untuk test yang benar-benar perlu memverifikasi persistensi data — mocking layer database dapat memberikan kepercayaan palsu jika query Anda salah.

### Pola AAA: Arrange, Act, Assert

Strukturkan setiap test dengan tiga bagian: Arrange (set up prasyarat — buat instance, mock dependency, seed data), Act (jalankan satu perilaku yang ditest), dan Assert (verifikasi hasilnya). Jaga agar setiap test fokus pada satu perilaku. Ketika sebuah test memiliki beberapa pasangan Act/Assert, ia menguji terlalu banyak hal dan akan memberi Anda diagnostik kegagalan yang buruk.

## Kapan TDD Menguntungkan (dan Kapan Memperlambat)

TDD tidak berlaku secara universal. Memahami di mana ia memberikan nilai terbesar dan di mana ia menambah gesekan membantu Anda menerapkannya secara pragmatis daripada dogmatis.

### Di Mana TDD Bersinar

TDD paling berharga untuk: logika bisnis dengan aturan kompleks (kalkulasi pajak, discount engine, logika validasi), kode yang akan sering di-refactor, perbaikan bug (tulis test yang mereproduksi bug terlebih dahulu, lalu perbaiki — test memastikan bug tidak pernah kembali), dan API dengan kontrak yang terdefinisi dengan baik. Dalam konteks ini, test suite menjadi dokumentasi yang dapat dieksekusi yang selalu terkini dengan kode.

> **Jangan TDD Kode Eksploratoris atau Komponen UI**

> TDD bekerja buruk untuk pemrograman eksploratoris (ketika Anda belum tahu desain yang tepat), komponen UI kompleks di mana output yang 'benar' bersifat visual dan subjektif, dan skrip sekali pakai yang tidak akan pernah dipelihara. Memaksakan TDD pada konteks ini menciptakan test yang sangat terikat pada detail implementasi dan rusak terus-menerus dengan setiap perubahan — kebalikan dari stabilitas yang dimaksudkan TDD untuk disediakan.

### Coverage Test: Metrik vs Tujuan

100% code coverage bukan tujuan TDD. Coverage adalah lagging indicator — ini memberi tahu Anda kode apa yang dieksekusi, bukan apakah test bermakna. Sangat mungkin memiliki 100% coverage tanpa assertion. Sebaliknya, targetkan coverage tinggi pada jalur kritis bisnis, dan perlakukan coverage rendah pada edge case sebagai sinyal risiko yang layak diselidiki.

## TDD dalam Proyek TypeScript Nyata

Menyiapkan TDD dalam proyek TypeScript memerlukan pemilihan test runner (Jest dengan ts-jest atau Vitest native), mengonfigurasi path alias agar sesuai dengan tsconfig Anda, menyiapkan pelaporan code coverage, dan mengintegrasikan test Anda ke dalam pipeline CI sehingga setiap pull request diverifikasi sebelum merge.

### Setup Jest dan Vitest untuk TypeScript

Vitest adalah pilihan modern untuk proyek TypeScript, terutama yang menggunakan Vite atau Next.js 15. Ia menggunakan konfigurasi yang sama dengan Vite, mendukung ESM secara native, dan memiliki watch mode yang jauh lebih cepat daripada Jest. Untuk Jest, gunakan ts-jest untuk kompilasi TypeScript langsung atau konfigurasikan Jest dengan Babel dan @babel/preset-typescript. Keduanya mendukung watch mode, coverage, dan snapshot testing.

### Mengorganisir Test dalam Codebase Besar

Letakkan unit test berdampingan dengan kode yang ditest (cart.ts berdampingan dengan cart.test.ts). Simpan integration test dalam direktori __tests__ terpisah atau folder /tests/integration khusus. Gunakan blok describe untuk mengelompokkan perilaku terkait. Gunakan it.todo() untuk mendokumentasikan test yang direncanakan. Jalankan unit test saat file disimpan (mode --watch) dan test integrasi penuh di CI.

> Gunakan mode 'ui' Vitest (npx vitest --ui) untuk test runner berbasis browser dengan visualisasi pass/fail real-time. Ini secara dramatis mempercepat siklus TDD saat mengerjakan logika bisnis yang kompleks.

## Membangun Budaya Testing

TDD bukan hanya praktik teknis — ini adalah budaya tim. Mulai dengan kesepakatan tim tentang ambang coverage minimum (80% untuk kode baru adalah floor pragmatis yang umum). Wajibkan test dalam pull request. Rayakan perbaikan bug yang didorong test. Jadikan test sebagai warga kelas satu dalam code review, memberikan mereka pengawasan yang sama dengan kode produksi. Test suite yang baik adalah aset tim yang menghemat berjam-jam debugging setiap sprint.

## Source links

- https://jestjs.io/docs/getting-started
- https://vitest.dev/guide/
- https://martinfowler.com/bliki/TestDrivenDevelopment.html
- https://kentcdodds.com/blog/write-tests

## FAQ

### Apa itu siklus Red-Green-Refactor dan mengapa penting?

Siklus Red-Green-Refactor adalah inti dari TDD: tulis test yang gagal (Red), tulis kode minimal agar test berhasil (Green), lalu bersihkan kode dan test-nya (Refactor). Disiplin hanya menulis kode minimum di fase Green mencegah over-engineering. Commit setelah setiap fase Green—sebelum refactoring—memberi Anda titik rollback yang bersih jika refactoring berjalan melenceng.

### Kapan sebaiknya menggunakan mock versus implementasi nyata dalam TDD?

Mock dependency eksternal seperti HTTP call, layanan email, dan payment processor karena lambat, tidak stabil, dan mahal saat diuji. Gunakan implementasi nyata untuk logika bisnis murni dan transformasi data. Untuk test yang benar-benar perlu memverifikasi persistensi data, lebih baik gunakan in-memory database daripada mocking seluruh layer database, karena mocking DB dapat memberikan kepercayaan palsu jika query Anda salah.

### Apakah TDD berlaku untuk semua jenis kode?

Tidak. TDD paling berharga untuk logika bisnis dengan aturan kompleks, kode yang akan sering di-refactor, perbaikan bug (tulis test yang mereproduksi bug terlebih dahulu), dan API dengan kontrak yang terdefinisi dengan baik. TDD bekerja buruk untuk pemrograman eksploratoris yang desainnya belum diketahui, komponen UI kompleks yang kebenarannya bersifat visual dan subjektif, serta skrip sekali pakai yang tidak akan dipelihara.

### Apakah 100% code coverage adalah tujuan TDD?

Tidak. Code coverage adalah lagging indicator yang menunjukkan kode mana yang dieksekusi, bukan apakah test bermakna—100% coverage dengan nol assertion sangat mungkin terjadi. Postingan ini merekomendasikan menargetkan coverage tinggi pada jalur kritis bisnis dan memperlakukan coverage rendah pada edge case sebagai sinyal risiko yang layak diselidiki, bukan angka yang harus dimanipulasi.

### Haruskah saya menggunakan Jest atau Vitest untuk TDD di proyek TypeScript?

Postingan ini merekomendasikan Vitest sebagai pilihan modern, terutama untuk proyek yang menggunakan Vite atau Next.js 15, karena menggunakan konfigurasi yang sama dengan Vite, mendukung ESM secara native, dan memiliki watch mode yang jauh lebih cepat daripada Jest. Jest tetap bisa digunakan dengan ts-jest untuk kompilasi TypeScript langsung atau Babel dengan @babel/preset-typescript. Keduanya mendukung watch mode, coverage, dan snapshot testing.

---

- HTML version: https://www.matthewswong.com/id/blog/test-driven-development-tdd/
- English: https://www.matthewswong.com/en/blog/test-driven-development-tdd/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
