Test strategy yang sustainable berarti tim bisa menjaga kualitas rilis secara konsisten tanpa membuat pipeline makin lambat, reviewer makin lelah, atau engineer harus terus-menerus memadamkan alarm palsu dari test suite. Tujuannya bukan mengejar jumlah test sebanyak mungkin, melainkan memastikan feedback yang cepat, reliabel, dan proporsional dengan risiko perubahan.
Pada tim kecil-menengah, masalah utamanya biasanya bukan ketiadaan test sama sekali, tetapi distribusi test yang buruk: terlalu banyak end-to-end test, terlalu sedikit unit/integration test yang tepat sasaran, CI menjalankan semua hal untuk semua perubahan, dan tidak ada aturan jelas ketika test gagal. Artikel ini membahas cara membangun test strategy yang sustainable dan manusiawi, dengan fokus pada apa yang wajib dijalankan, apa yang cukup dijalankan terjadwal, dan bagaimana menjaga suite tetap berguna dalam jangka panjang.
Prinsip dasar: reliabilitas tidak boleh dibayar dengan kelelahan tim
Strategi testing yang sehat harus memenuhi tiga syarat:
- Cepat memberi umpan balik: engineer tahu lebih awal apakah perubahan merusak perilaku penting.
- Stabil: kegagalan test benar-benar mengindikasikan masalah, bukan noise.
- Murah dirawat: menambah atau memperbarui test tidak lebih sulit daripada memperbaiki kode produksi.
Jika salah satu syarat gagal, tim akan mulai menghindari test, menekan tombol rerun tanpa investigasi, atau menunda perbaikan flaky test karena dianggap biasa. Pada titik itu, test suite masih ada, tetapi nilainya menurun drastis.
Target praktisnya bukan “100% otomatis”, melainkan “setiap perubahan penting mendapat perlindungan yang sesuai risikonya, dengan biaya operasional yang masuk akal”.
Menyusun piramida testing yang realistis
Piramida testing membantu membagi tanggung jawab antar level test. Untuk tim kecil-menengah, bentuk yang paling sustainable biasanya:
- Banyak unit test untuk logika murni, validasi, transformasi data, permission rule, formatter, parser, dan domain logic.
- Sejumlah integration test untuk kontrak antar komponen: API ke service, service ke database, event ke handler, cache behavior, repository, atau serialisasi.
- Sedikit end-to-end test hanya untuk alur bisnis paling kritis.
1. Unit test: murah, cepat, dan spesifik
Unit test cocok untuk perilaku yang deterministik dan tidak membutuhkan jaringan, filesystem, atau database sungguhan. Test ini sebaiknya menjadi lapisan terbesar karena paling cepat dijalankan dan paling murah di-debug.
Contoh area yang ideal untuk unit test:
- Perhitungan harga, diskon, pajak, atau scoring.
- Validasi payload sebelum masuk ke layer lain.
- Pemetaan status internal ke respons API.
- Permission logic dan rule engine sederhana.
2. Integration test: menjaga kontrak penting
Integration test dibutuhkan ketika bug sering muncul bukan karena logika lokal, tetapi karena interaksi antar komponen. Misalnya query database yang berubah, serialisasi JSON yang tidak sesuai kontrak, migrasi schema, atau service yang bergantung pada message queue.
Lapisan ini penting karena unit test sering terlalu optimistis ketika semua dependency dimock. Jika terlalu banyak mock, test bisa “hijau” sementara sistem nyata gagal.
3. End-to-end test: batasi pada jalur bisnis kritis
End-to-end test paling mahal dijalankan dan paling rentan flaky karena melibatkan banyak lapisan sekaligus. Karena itu, gunakan untuk skenario yang benar-benar kritis, misalnya:
- Login atau autentikasi dasar.
- Checkout atau pembuatan transaksi.
- Alur submit form utama yang menghasilkan data penting.
- Role-based access untuk aksi sensitif.
Kesalahan umum adalah menjadikan E2E sebagai pengganti integration test. Hasilnya: pipeline lambat, diagnosis sulit, dan setiap perubahan UI kecil memicu banyak perbaikan test.
Apa yang wajib dijalankan di CI
CI sebaiknya hanya menjalankan test yang memberi sinyal kuat terhadap regresi dengan waktu tunggu yang masih masuk akal. Tidak semua test harus blocking untuk setiap pull request.
Kriteria test yang wajib blocking di CI
- Cepat: idealnya selesai dalam beberapa menit, bukan puluhan menit.
- Stabil: jarang gagal karena environment atau timing.
- Relevan dengan perubahan harian: menyentuh area yang sering berubah atau sering rusak.
- Melindungi jalur bisnis inti: autentikasi, pembayaran, izin akses, penyimpanan data penting.
Dalam praktiknya, set minimum yang sering efektif adalah:
- Linting dan static analysis dasar.
- Unit test seluruh modul yang cepat.
- Integration test untuk kontrak data dan API penting.
- Sedikit smoke E2E untuk jalur kritis.
Test yang tidak harus blocking setiap PR
- Regression suite besar yang mahal dijalankan.
- Cross-browser atau multi-device matrix penuh.
- Load test dan stress test.
- Security scan yang lebih berat.
- E2E untuk fitur non-kritis yang jarang berubah.
Test seperti ini bisa dijalankan terjadwal, sebelum rilis, atau hanya saat area terkait berubah. Intinya adalah memisahkan fast feedback dari deep verification.
Contoh pembagian job CI
pull_request_checks:
- format/lint
- static analysis
- unit tests
- critical integration tests
- smoke e2e
nightly_checks:
- full integration suite
- extended e2e suite
- cross-environment compatibility checks
- performance baseline checks
pre_release_checks:
- release regression suite
- migration verification
- rollback/smoke deployment checksPembagian seperti ini bekerja karena setiap lapisan punya tujuan berbeda. PR butuh sinyal cepat; nightly butuh cakupan lebih luas; pre-release butuh verifikasi yang lebih konservatif.
Risk-based testing: mencegah regresi tanpa menguji semua hal
Risk-based testing membantu tim menentukan area mana yang wajib dilindungi lebih ketat. Daripada menulis test merata ke semua bagian, tim menilai perubahan berdasarkan dampak dan probabilitas rusak.
Matriks prioritas test
| Dampak | Probabilitas | Prioritas | Tindakan |
|---|---|---|---|
| Tinggi | Tinggi | P1 | Unit + integration + smoke E2E, wajib blocking di CI |
| Tinggi | Rendah | P2 | Unit + integration, E2E jika jalur bisnis langsung terpengaruh |
| Rendah | Tinggi | P2 | Unit test fokus pada area rawan, monitoring pasca-rilis |
| Rendah | Rendah | P3 | Test seperlunya, cukup coverage lokal atau manual check terarah |
Cara menilai dampak dan probabilitas
Dampak biasanya tinggi jika perubahan menyentuh:
- Autentikasi dan otorisasi.
- Pembayaran atau billing.
- Data yang tidak mudah dipulihkan.
- API publik atau kontrak eksternal.
- Fitur yang langsung memengaruhi pendapatan atau operasional.
Probabilitas biasanya tinggi jika:
- Kodenya kompleks atau sulit dipahami.
- Banyak dependency atau kondisi bercabang.
- Sering diubah oleh banyak orang.
- Pernah punya histori bug regresi.
- Masih bergantung pada mock berat dan test lapisan atas.
Dengan pendekatan ini, tim tidak perlu memperlakukan semua perubahan secara sama. Perubahan copy UI sederhana tidak perlu memicu suite besar yang sama dengan perubahan pada alur checkout.
Mengurangi flaky test secara sistematis
Flaky test adalah salah satu penyebab utama test suite terasa melelahkan. Masalahnya bukan hanya rerun yang membuang waktu, tetapi rusaknya kepercayaan tim terhadap sinyal CI.
Penyebab umum flaky test
- Ketergantungan waktu: test bergantung pada jam lokal, timezone, sleep, atau timeout yang rapuh.
- State bocor antar test: database, cache, file, singleton, atau environment variable tidak di-reset dengan benar.
- Ketergantungan jaringan: memanggil service eksternal nyata saat test.
- Asinkron tidak ditunggu dengan benar: queue, event, background job, atau render UI belum selesai saat assertion dilakukan.
- Data test tidak deterministik: random value tanpa seed atau data bersama yang saling bertabrakan.
Praktik untuk menurunkan flaky rate
- Bekukan sumber nondeterministik
Mock waktu, seed random, dan isolasi dependency eksternal. - Gunakan polling berbatas, bukan sleep statis
Jika perlu menunggu kondisi asinkron, tunggu sampai kondisi terpenuhi dalam batas waktu yang jelas, bukan menebak durasi dengansleep. - Reset state secara eksplisit
Pastikan database, cache, queue, dan filesystem test dibersihkan tiap test atau tiap suite. - Hindari assertion yang terlalu rapuh
Untuk UI atau respons kompleks, fokus pada perilaku penting, bukan detail incidental yang sering berubah. - Karantina flaky test dengan tiket perbaikan
Jangan biarkan test flaky terus menjadi blocking tanpa pemilik. Jika harus diisolasi sementara, beri batas waktu dan penanggung jawab.
Contoh pola yang lebih aman untuk asinkron
// Hindari pola seperti ini
sleep(2)
assertJobProcessed(orderId)
// Lebih aman: tunggu kondisi dengan timeout terbatas
waitUntil(() => jobProcessed(orderId), timeout=5000, interval=100)
assertJobProcessed(orderId)Pola ini bekerja karena durasi proses asinkron bisa berubah tergantung beban mesin CI. sleep statis sering terlalu pendek saat CI lambat, atau terlalu panjang saat sebenarnya tidak dibutuhkan.
Workflow triage saat test gagal
Test strategy yang sustainable perlu aturan operasional yang jelas ketika pipeline merah. Jika tidak, kegagalan akan ditangani secara ad hoc: rerun berulang, saling lempar tanggung jawab, atau merge tetap dilakukan karena dianggap “CI lagi rewel”.
Tujuan triage
- Membedakan antara bug nyata, flaky test, dan masalah environment.
- Menentukan siapa yang harus bertindak dan seberapa cepat.
- Mencegah failure yang sama berulang tanpa perbaikan akar masalah.
Workflow triage yang bisa diterapkan
- Identifikasi jenis kegagalan
Apakah assertion berubah? Apakah timeout? Apakah service test environment tidak sehat? Apakah hanya gagal di satu runner? - Cek riwayat
Apakah test yang sama pernah gagal sebelumnya tanpa perubahan terkait? Jika ya, kemungkinan flaky lebih tinggi. - Klasifikasikan
Label sebagai product bug, test bug, atau environment issue. - Tentukan tindakan langsung
Product bug: perbaiki kode atau rollback perubahan.
Test bug: perbaiki test sebelum merge jika test blocking.
Environment issue: perbaiki infrastruktur CI dan dokumentasikan dampaknya. - Catat akar masalah
Minimal di tiket atau template insiden internal: gejala, area terdampak, penyebab, dan pencegahan.
Aturan penting untuk tim
- Jangan menjadikan rerun sebagai solusi default.
- Jika test flaky terkonfirmasi, buat tiket perbaikan dengan prioritas yang jelas.
- Jika sebuah test terlalu sering gagal tanpa sinyal nyata, lebih baik nonaktifkan sementara dengan kontrol yang jelas daripada membiarkannya merusak kepercayaan tim.
Menonaktifkan test memang trade-off. Namun test yang sering berbohong juga berbahaya. Kuncinya adalah isolasi sementara yang terukur, bukan pembiaran permanen.
Checklist PR untuk menjaga kualitas tanpa menambah beban berlebih
Checklist PR membantu menyamakan ekspektasi antar engineer dan reviewer. Checklist yang baik harus singkat, relevan, dan bisa dijawab secara objektif.
Contoh checklist PR
- Perubahan ini menyentuh area berisiko tinggi atau tidak?
- Apakah ada unit test untuk logika baru atau cabang kondisi penting?
- Apakah integration test diperlukan untuk kontrak API, database, queue, atau cache?
- Jika ada perubahan perilaku user-facing, apakah smoke E2E perlu diperbarui?
- Apakah perubahan ini berpotensi memicu race condition, timeout, atau efek asinkron?
- Apakah dependency eksternal dimock atau diisolasi dengan benar saat test?
- Apakah test baru deterministik dan tidak bergantung pada urutan eksekusi?
- Jika bug fix regresi, apakah ada test yang benar-benar gagal sebelum perbaikan dan hijau sesudahnya?
Checklist ini efektif karena memaksa diskusi pada risiko nyata, bukan sekadar “sudah ada test atau belum”.
Tanda bahwa suite test sudah terlalu mahal dirawat
Banyak tim baru sadar ada masalah setelah kecepatan delivery menurun. Beberapa sinyal berikut menunjukkan suite test sudah melewati biaya yang sehat:
- Pipeline terlalu lama untuk ukuran frekuensi commit tim, sehingga engineer menunggu terlalu sering.
- Rerun menjadi kebiasaan, bukan pengecualian.
- Reviewer sulit memahami maksud test karena test terlalu implementasi-sentris.
- Perubahan kecil memecahkan banyak test tak terkait.
- Lebih banyak waktu untuk memperbaiki test daripada memvalidasi perilaku.
- Coverage tinggi tetapi bug regresi tetap lolos, menandakan distribusi test salah sasaran.
- E2E mendominasi suite dan integration test minim.
Apa yang perlu dilakukan jika sudah terlanjur mahal
- Audit test yang paling sering gagal dan paling lama berjalan.
- Kelompokkan test berdasarkan nilai bisnis, stabilitas, dan biaya eksekusi.
- Pindahkan assertion yang terlalu detail dari E2E ke integration atau unit test.
- Hapus duplikasi antar lapisan test.
- Tetapkan budget waktu CI dan pertahankan sebagai constraint engineering.
Prinsipnya sederhana: setiap test harus punya alasan keberadaan yang jelas. Jika dua test melindungi risiko yang sama, pilih lapisan yang paling murah dan paling stabil.
Contoh kebijakan praktis untuk tim kecil-menengah
Berikut contoh kebijakan yang cukup realistis diterapkan tanpa proses yang terlalu berat:
- PR wajib lulus: lint, static analysis, unit test, integration test kritis, dan 3-5 smoke E2E utama.
- Perubahan P1: wajib ada test regresi di level terendah yang masuk akal, plus verifikasi kontrak jika menyentuh integrasi.
- Flaky test: jika gagal dua kali dalam konteks berbeda tanpa perubahan relevan, tandai sebagai kandidat flaky dan buka tiket investigasi.
- Nightly: jalankan full regression suite dan laporkan test paling lambat serta paling sering gagal.
- Setiap retrospektif release: review bug yang lolos, lalu perbaiki distribusi test, bukan sekadar menambah E2E baru.
Kebijakan ini membantu menjaga disiplin tanpa membuat setiap perubahan harus melewati prosedur yang sama beratnya.
Penutup
Test strategy yang sustainable bukan soal menambah sebanyak mungkin test, tetapi soal menempatkan test pada level yang tepat, memutuskan mana yang wajib di CI, dan menolak normalisasi flaky test sebagai bagian dari kerja harian. Untuk tim kecil-menengah, pendekatan yang paling sehat biasanya adalah memperbesar unit dan integration test, membatasi E2E pada jalur kritis, dan memakai risk-based testing untuk memfokuskan energi di area yang benar-benar penting.
Jika tim Anda merasa kualitas rilis hanya bisa dijaga dengan suite yang berat atau budaya lembur saat pipeline bermasalah, biasanya yang perlu diubah bukan semangat tim, melainkan desain strategi testing-nya. Mulailah dari audit sederhana: test mana yang paling mahal, paling sering bohong, dan paling sedikit memberi nilai. Dari situ, buat CI lebih selektif, triage lebih jelas, dan suite lebih dapat dipercaya.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!