Unit testing adalah fondasi untuk kode yang lebih andal: ia memverifikasi unit terkecil dari aplikasi secara otomatis sehingga bug terdeteksi lebih cepat. Panduan ini disusun secara ringkas dan aplikatif. Isinya mencakup waktu yang tepat untuk menulis unit test, strategi prioritas beserta contohnya, serta cara menyelaraskan pengujian unit dengan QA manual guna mempercepat rilis tanpa mengorbankan kualitas.
Mengapa kita harus repot-repot bikin unit test kalau aplikasi sudah dites manual oleh QA?
Mungkin kamu bertanya-tanya, kalau QA sudah mengetes manual, kenapa repot bikin unit test? Jawabannya bukan untuk menggantikan QA, melainkan melengkapi: unit test menemukan regresi cepat, memudahkan refactor, dan mengurangi waktu debugging. Selain itu, unit test berjalan otomatis dan konsisten, sedangkan tes manual rentan lupa atau variansi antar tester. Sekarang mari kita bahas alasan utama dan langkah praktis di bab pertama.

💻 Mulai Belajar Pemrograman
Belajar pemrograman di Dicoding Academy dan mulai perjalanan Anda sebagai developer profesional.
Daftar SekarangMengapa Unit Testing Penting untuk Stabilitas Aplikasi
Unit test fokus pada bagian kode paling kecil, misalnya satu fungsi atau satu class. Berbeda dengan integration test yang menguji interaksi antar modul, dan system test yang menguji seluruh aplikasi dari ujung ke ujung. Semakin kecil unit yang diuji, semakin mudah menemukan sumber masalah.
Dengan unit testing, bug bisa terdeteksi lebih awal sebelum menyebar ke banyak modul. Setiap test juga menjadi dokumentasi hidup tentang perilaku fungsi, termasuk edge case. Saat kamu melakukan refactor, test yang sama memastikan perilaku lama tetap terjaga meski implementasi berubah total. Ini memberi feedback loop yang cepat, jauh lebih cepat daripada menunggu QA manual.
Bayangkan fitur perhitungan diskon yang sudah “lulus” QA manual. Beberapa sprint kemudian, ada perubahan kecil di logika harga, dan QA hanya menguji skenario umum, bukan semua kombinasi. Tanpa unit test, diskon khusus pelanggan lama bisa rusak tanpa ada yang sadar sampai komplain masuk. Dengan kombinasi QA manual dan unit test, test regresi otomatis akan langsung gagal saat logika lama terlanggar, sehingga kamu bisa memperbaiki sebelum rilis dan sebelum merusak metrik bisnis.
Menentukan Unit Test yang Efektif dan Terukur
Untuk menentukan unit test yang efektif, mulai dari fungsi paling kritis: perhitungan harga, otorisasi, dan aturan diskon. Tambahkan logika bisnis yang sering berubah, karena area ini paling rawan regresi saat fitur baru masuk. Setelah itu, cari boundary cases: nilai minimum–maksimum, input kosong, dan kombinasi ekstrem.
Setiap unit test harus punya kriteria pass/fail yang jelas dan deterministik. Hindari akses langsung ke database, filesystem, atau API eksternal; gunakan mock atau stub yang ringan. Contoh sederhana:
|
1 2 3 4 |
test('totalPrice menghitung dengan benar', () => { const total = totalPrice(2, 50000, 0.1); expect(total).toBe(90000); }); |
Jaga agar tes kecil dan cepat, sehingga nyaman dijalankan di CI dan lokal berkali-kali. Hindari tes yang terlalu terikat ke struktur internal kelas atau fungsi, supaya refactor tidak membuat ratusan tes rusak tanpa ada perubahan perilaku nyata.
Strategi Automasi Mocking dan Test Doubles
Setelah tahu apa yang perlu diuji, langkah berikutnya adalah menjaga setiap unit test tetap terisolasi. Di sini kamu memakai test doubles seperti mock, stub, dan fake untuk menggantikan dependency eksternal. Stub cocok untuk mengembalikan data statis, misalnya respons API yang sudah diprediksi. Fake berguna untuk versi sederhana komponen, seperti in-memory repository pengganti database. Mock dipakai saat kamu ingin memastikan sebuah metode dipanggil dengan argumen tertentu.
|
1 2 3 4 5 6 7 8 9 10 |
def test_create_user_sends_email(mocker): # arrange email_service = mocker.Mock() user_service = UserService(email_service) # act user_service.create("alice@example.com") # assert email_service.send.assert_called_once_with("alice@example.com") |
Pola arrange-act-assert membuat struktur test rapi dan mudah dibaca. Fixture membantu menyiapkan data atau objek berulang, sehingga test fokus ke perilaku, bukan ke setup. Parametrized tests berguna untuk menguji banyak variasi input tanpa menulis fungsi test terpisah. Hati-hati dengan over-mocking, karena bisa membuat test rapuh dan terlalu jauh dari perilaku nyata sistem.
Untuk alur penting yang menyentuh beberapa komponen sekaligus, tambahkan lightweight integration test. Misalnya, menguji alur pembuatan pesanan dari service ke database sungguhan, tetapi tetap mem-mock layanan pembayaran eksternal. Strategi ini menjaga kecepatan test otomatis sekaligus menurunkan risiko bug di production, dan menyiapkan dasar yang kuat sebelum test kamu dihubungkan ke CI pipeline.
Integrasi Unit Test dengan CI dan Praktik Terbaik
Integrasi dengan CI membuat unit test benar-benar hidup, bukan hanya file yang dilupakan di repo. Konfigurasikan pipeline agar menjalankan unit test di setiap pull request dan setiap build ke main branch. Misalnya, di GitHub Actions:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
name: CI on: pull_request: push: branches: [ main ] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 20 - run: npm ci - run: npm test -- --ci --reporters=jest-junit |
Tetapkan aturan sederhana: PR tidak boleh di-merge jika job test gagal, dan gunakan status check wajib di platform Git kamu. Threshold awal bisa realistis, misalnya minimal 60–70% coverage untuk modul kritis, lalu dinaikkan bertahap. Jadikan review test bagian eksplisit dari code review: reviewer mengecek skenario negatif, penggunaan mock, dan apakah bug yang pernah muncul sudah punya test.
Untuk flaky test, tandai cepat, isolasi, dan perbaiki; hindari membiasakan rerun otomatis tanpa akar masalah jelas. Sementara unit test berjalan otomatis di CI, QA manual bisa fokus pada exploratory testing dan end-to-end flow yang sulit diotomasi. Pola kerjanya: developer menulis test sebelum atau saat perbaikan, push ke repo, CI menjalankan dalam hitungan menit, dan QA mendapat build yang sudah “disaring” oleh lapisan unit test.
Penutup
Unit testing bukan beban tambahan tetapi investasi: mempercepat pengembangan, menurunkan bug production, dan membuat tim lebih percaya diri melakukan perubahan. Dengan strategi prioritas, automasi, dan integrasi CI, unit test akan melengkapi QA manual — bukan menggantikannya — dan meningkatkan kecepatan rilis tanpa mengorbankan kualitas.
