Strategi test cadence adalah cara mengatur kapan jenis test tertentu dijalankan, seberapa ketat hasilnya dipakai sebagai gate, dan kapan sinyal test dianggap cukup kuat untuk memblokir perubahan. Tujuannya bukan sekadar “menjalankan lebih banyak test”, tetapi membangun ritme verifikasi yang memberi umpan balik cepat tanpa membanjiri tim dengan hasil yang tidak stabil.

Ada analogi yang berguna dari riset tentang slow breathing: ritme yang terkontrol dapat membantu menurunkan noise dalam pengambilan keputusan. Dalam pipeline software, prinsipnya mirip. Jika verifikasi terlalu padat, acak, dan tidak dibedakan berdasarkan nilai sinyalnya, tim justru menerima banyak alarm palsu, membiasakan diri mengabaikan kegagalan, lalu regresi nyata lebih mudah lolos. Karena itu, ritme test harus dirancang, bukan dibiarkan tumbuh liar.

Mengapa test cadence penting untuk flaky test dan regresi

Flaky test adalah test yang kadang lulus, kadang gagal, tanpa perubahan kode yang relevan. Masalah utamanya bukan hanya kegagalan teknis, tetapi rusaknya kepercayaan terhadap pipeline. Ketika developer terbiasa melihat test merah yang “mungkin cuma flake”, kualitas gate CI menurun.

Di sisi lain, menekan flakiness dengan mengurangi cakupan test secara agresif juga berbahaya karena regresi bisa tidak terdeteksi. Solusinya adalah membedakan ritme eksekusi berdasarkan karakteristik test:

  • Fast feedback untuk sinyal cepat dan deterministik.
  • Pre-merge untuk verifikasi perubahan sebelum masuk ke branch utama.
  • Nightly untuk skenario lebih berat, lebih luas, atau lebih mahal.
  • Release verification untuk jalur bisnis dan operasional paling kritis sebelum rilis.

Dengan pembagian ini, tim tidak memaksa semua test menjadi gate di setiap saat. Hasilnya: waktu tunggu berkurang, flakiness lebih mudah diisolasi, dan regresi penting tetap tertangkap.

Klasifikasi suite: jangan campur semua test dalam satu ritme

1. Fast feedback

Tahap ini idealnya dijalankan di mesin developer dan pada pull request awal. Isinya:

  • Unit test yang murni dan cepat.
  • Linting dan static analysis.
  • Contract test kecil yang deterministik.
  • Smoke test lokal untuk area yang diubah.

Kriterianya sederhana: cepat, stabil, mudah di-debug, dan tidak bergantung pada jaringan publik atau kondisi lingkungan yang sulit dikontrol.

2. Pre-merge

Ini adalah gate utama sebelum merge. Isinya biasanya:

  • Semua fast feedback checks.
  • Integration test penting terhadap database, cache, queue, atau API internal.
  • Test jalur kritis yang sering berubah.
  • Subset end-to-end yang benar-benar mewakili skenario utama pengguna.

Pre-merge harus tetap selektif. Jika semua test berat dipindahkan ke tahap ini, waktu tunggu merge naik, retry manual meningkat, dan developer mulai mencari jalan pintas.

3. Nightly

Nightly cocok untuk test yang luas tetapi tidak efektif dijadikan gate setiap commit:

  • End-to-end skala besar.
  • Compatibility test lintas konfigurasi.
  • Load baseline ringan atau soak test pendek.
  • Migration rehearsal, backup-restore verification, atau skenario recovery.

Nightly berguna untuk mendeteksi regresi sistemik yang tidak tampak di subset pre-merge, tetapi hasilnya perlu ditangani dengan disiplin. Nightly yang selalu merah tanpa tindak lanjut hanya menambah noise.

4. Release verification

Sebelum rilis, lakukan verifikasi yang fokus pada risiko bisnis dan operasional:

  • Smoke test deployment.
  • Validasi konfigurasi production-like.
  • Jalur transaksi utama, autentikasi, otorisasi, billing, dan observability.
  • Rollback path atau verifikasi migrasi yang irreversible.

Release verification bukan salinan penuh nightly. Ia harus singkat, terukur, dan menargetkan area dengan dampak tertinggi jika gagal.

Prinsip desain suite yang menekan flakiness

Isolasi: state bersama adalah sumber masalah paling umum

Banyak flaky test lahir dari state yang bocor antar test: database tidak dibersihkan, cache tertinggal, jam sistem berubah, urutan eksekusi memengaruhi hasil, atau port jaringan bentrok. Test yang baik harus bisa dijalankan sendiri maupun paralel tanpa hasil berubah.

Praktik yang membantu:

  • Reset database per test case atau per suite dengan strategi yang konsisten.
  • Gunakan data fixture yang eksplisit, bukan bergantung pada sisa test sebelumnya.
  • Stub atau mock dependensi eksternal bila tujuan test bukan menguji layanan itu.
  • Kontrol waktu dengan fake clock bila ada logika berbasis waktu.
  • Hindari assertion sensitif urutan jika urutan bukan kontrak bisnis.

Determinisme lebih penting daripada cakupan semu

Test yang “menyentuh semuanya” tetapi bergantung pada internet, waktu aktual, dan kondisi acak sering terlihat meyakinkan namun menghasilkan sinyal buruk. Lebih baik punya cakupan yang sedikit lebih sempit tetapi hasilnya stabil, lalu melengkapi area lain di nightly atau release verification.

Karantina flaky test, jangan dibiarkan di gate utama

Jika sebuah test terbukti flaky, jangan biarkan terus memblokir merge sambil semua orang menebak-nebak. Pindahkan ke quarantine lane dengan aturan yang jelas:

  • Status flaky harus dicatat dan diberi owner.
  • Test keluar dari gate pre-merge sampai diperbaiki.
  • Test tetap dijalankan terpisah agar tidak hilang dari radar.
  • Ada target waktu perbaikan, bukan karantina permanen.

Catatan: Karantina bukan alasan untuk menerima kualitas buruk. Fungsinya adalah memulihkan integritas gate CI sambil memastikan perbaikan bisa diprioritaskan.

Retry boleh, tetapi harus bertanggung jawab

Retry sering disalahgunakan untuk “menghijaukan” pipeline. Padahal retry tanpa aturan hanya menyembunyikan ketidakstabilan. Gunakan retry secara terbatas dan transparan:

  • Retry hanya untuk kategori error yang memang rentan transient, misalnya timeout jaringan pada lingkungan integrasi.
  • Batasi jumlah retry kecil dan catat setiap retry sebagai sinyal.
  • Jangan retry unit test murni; jika flaky, masalahnya hampir pasti ada pada test atau state.
  • Bedakan hasil “pass after retry” dari “pass first run” dalam metrik.

Jika sebuah test sering lulus setelah retry, itu bukan sehat. Itu hanya flakiness yang belum dibayar lunas.

Merancang aturan gate CI yang efektif

Gate CI harus memblokir perubahan berdasarkan risiko, bukan berdasarkan kebiasaan historis. Beberapa aturan yang umum dan efektif:

  • Wajib lulus: unit test inti, static analysis, dan integration test deterministik untuk area kritis.
  • Boleh informasional: test eksperimen, suite besar yang sedang distabilkan, atau compatibility matrix yang belum matang.
  • Karantina otomatis: test yang melewati ambang flakiness tertentu dipindah keluar dari gate utama.
  • Blok merge jika coverage jalur kritis turun, bukan sekadar coverage total global.

Fokus pada jalur kritis lebih berguna daripada terpaku pada satu angka coverage. Misalnya, endpoint pembayaran, login, sinkronisasi data, atau proses pembuatan invoice layak punya perlindungan lebih ketat dibanding area internal yang jarang dipakai.

Contoh pembagian gate

  • PR kecil non-kritis: lint, unit, subset integration berdasarkan file yang berubah.
  • PR area kritis: semua di atas ditambah smoke end-to-end dan contract test penuh.
  • Merge ke main: pre-merge penuh, publish artifact, jalankan subset smoke terhadap environment ephemeral.
  • Nightly: suite luas, compatibility matrix, test stabilitas.
  • Pra-rilis: release verification dengan checklist risiko produksi.

Test impact analysis: jalankan lebih cerdas, bukan selalu lebih banyak

Test impact analysis membantu menentukan test mana yang perlu dijalankan berdasarkan perubahan kode. Ini bukan berarti hanya menjalankan test “dekat” secara folder, tetapi memetakan dependensi antara komponen, modul, kontrak API, schema database, atau area UI.

Pendekatan praktis:

  • Mulai dari aturan sederhana: perubahan pada folder backend tertentu memicu suite integration terkait.
  • Tambahkan peta dependensi untuk shared library, schema, dan kontrak antarlayanan.
  • Untuk area sangat kritis, tetap jalankan full critical path walaupun perubahan tampak kecil.

Kelebihannya adalah waktu pre-merge lebih singkat. Trade-off-nya, pemetaan dependensi yang buruk bisa membuat regresi lolos. Karena itu, fallback ke nightly dan release verification tetap penting.

Kapan test dipindah antar tahap

Ini aturan praktis yang bisa dipakai:

  • Pindah ke fast feedback jika test cepat, stabil, dan sering membantu developer saat coding.
  • Pindah ke pre-merge jika test menangkap regresi nyata pada area yang sering disentuh dan tingkat flakiness rendah.
  • Pindah ke nightly jika test mahal, lama, butuh matrix luas, atau masih belum cukup stabil untuk gate ketat.
  • Pindah ke release verification jika test terkait risiko bisnis/operasional tinggi tetapi tidak perlu diulang setiap commit.
  • Keluarkan dari gate jika test terbukti flaky sampai akar masalah diperbaiki.

Jangan memindahkan test hanya karena “terasa lambat”. Putuskan berdasarkan data: waktu eksekusi, frekuensi kegagalan, nilai deteksi regresi, dan biaya investigasi.

Contoh workflow CI yang realistis

Berikut contoh workflow generik untuk menggambarkan pembagian ritme. Formatnya sengaja dibuat netral agar bisa diterapkan di GitHub Actions, GitLab CI, Jenkins, atau sistem lain dengan sedikit penyesuaian.

stages:
  - fast_feedback
  - pre_merge
  - nightly
  - release_verification

fast_feedback:
  triggers: [pull_request, push]
  jobs:
    - lint
    - static_analysis
    - unit_tests
    - impacted_small_integration_tests
  gate: required

pre_merge:
  triggers: [pull_request]
  conditions:
    - target_branch in [main, release/*]
  jobs:
    - full_unit_tests
    - critical_integration_tests
    - contract_tests
    - smoke_e2e_subset
  gate: required

quarantine_flaky:
  triggers: [pull_request, nightly]
  jobs:
    - known_flaky_tests
  gate: non_blocking
  alerts:
    - notify_test_owner

nightly:
  triggers: [schedule]
  jobs:
    - full_e2e_suite
    - compatibility_matrix
    - migration_rehearsal
    - recovery_smoke
  gate: blocking_for_release_branch_only

release_verification:
  triggers: [manual, tag, release_candidate]
  jobs:
    - deploy_smoke
    - critical_user_journeys
    - auth_and_permission_checks
    - observability_checks
    - rollback_verification
  gate: required_for_release

Poin penting dari workflow di atas:

  • Suite flaky dipisahkan dari gate utama.
  • Nightly tidak otomatis memblokir semua alur, tetapi dapat dijadikan wajib untuk branch rilis.
  • Release verification berfokus pada risiko produksi, bukan pengulangan seluruh suite.

Checklist audit suite: apakah ritme test Anda sehat?

Gunakan checklist berikut untuk mengaudit suite yang sudah ada:

  1. Apakah setiap test punya tujuan yang jelas: unit, integration, contract, end-to-end, atau operasional?
  2. Apakah ada test gate yang sering gagal tetapi jarang menemukan bug nyata?
  3. Apakah test menggunakan state bersama, waktu aktual, atau ketergantungan jaringan yang tidak dikontrol?
  4. Apakah “pass after retry” dicatat dan dianalisis?
  5. Apakah ada daftar test karantina beserta owner dan tenggat perbaikannya?
  6. Apakah jalur kritis bisnis memiliki proteksi di pre-merge dan pra-rilis?
  7. Apakah test mahal dijalankan terlalu dini, sehingga memperlambat feedback tanpa nilai sepadan?
  8. Apakah test impact analysis sudah cukup aman untuk area yang sering berubah?
  9. Apakah kegagalan nightly benar-benar ditinjau, atau hanya menjadi dashboard merah permanen?
  10. Apakah aturan gate berbeda untuk branch fitur, main, dan release?

Metrik yang perlu dipantau

Tanpa metrik, diskusi tentang flakiness biasanya berubah menjadi opini. Beberapa metrik yang berguna:

  • Failure rate per test: seberapa sering test gagal.
  • Pass-after-retry rate: indikator flakiness tersembunyi.
  • Mean time to diagnose: waktu rata-rata investigasi kegagalan.
  • Mean time to fix flaky test: apakah karantina benar-benar diselesaikan.
  • Pipeline duration per stage: waktu fast feedback, pre-merge, nightly, dan pra-rilis.
  • Merge delay caused by test failures: dampak langsung ke throughput tim.
  • Regression escape rate: bug yang lolos meski pipeline hijau.
  • Critical path coverage: apakah jalur bisnis penting benar-benar terlindungi.

Jangan hanya melihat coverage total. Dua suite dengan angka coverage sama bisa punya kualitas proteksi yang sangat berbeda jika salah satunya menutupi jalur kritis dengan baik dan yang lain tidak.

Kesalahan umum saat menerapkan strategi test cadence

1. Semua test dijadikan wajib di setiap commit

Ini tampak aman, tetapi biasanya berujung pada pipeline lambat dan kelelahan alarm. Developer mulai menganggap merah sebagai hal biasa.

2. Retry dipakai untuk menutup masalah desain test

Jika retry menjadi solusi utama, akar masalah seperti race condition, ketergantungan eksternal, atau kebocoran state tidak pernah selesai.

3. Quarantine tanpa owner

Test yang dikarantina tanpa penanggung jawab hampir pasti menjadi kuburan suite. Karantina harus punya tiket, prioritas, dan batas waktu.

4. Mengandalkan coverage global

Coverage tinggi tidak otomatis berarti regresi penting tertangkap. Prioritaskan area kritis dan skenario bernilai tinggi.

5. Nightly dijalankan, tetapi tidak memengaruhi keputusan

Jika nightly gagal terus tanpa konsekuensi, ia berubah dari alat deteksi menjadi dekorasi dashboard.

Panduan implementasi bertahap

Jika organisasi Anda sudah punya suite besar dan tidak stabil, jangan mencoba memperbaiki semuanya sekaligus. Lakukan bertahap:

  1. Petakan suite yang ada berdasarkan jenis, durasi, dan tingkat flakiness.
  2. Tentukan critical path yang harus dilindungi di pre-merge dan pra-rilis.
  3. Pisahkan flaky test ke karantina agar gate utama kembali dipercaya.
  4. Kurangi dependensi tak terkontrol pada test pre-merge.
  5. Terapkan retry terbatas dan ukur pass-after-retry.
  6. Tambahkan test impact analysis secara konservatif.
  7. Tinjau ulang setiap bulan test mana yang layak dipromosikan atau diturunkan tahapnya.

Pendekatan ini biasanya lebih efektif daripada menambah alat baru tanpa merapikan ritme yang sudah ada.

Penutup

Strategi test cadence yang baik menempatkan test pada ritme yang sesuai dengan nilai sinyalnya: cepat untuk feedback coding, cukup ketat sebelum merge, lebih luas di nightly, dan terfokus pada risiko saat verifikasi rilis. Dengan klasifikasi yang jelas, isolasi yang disiplin, karantina untuk flaky test, retry yang bertanggung jawab, dan gate CI berbasis risiko, tim bisa menurunkan noise sekaligus mencegah regresi nyata lolos ke produksi.

Jika pipeline Anda terasa “sibuk” tetapi tidak membantu pengambilan keputusan, kemungkinan masalahnya bukan kurangnya test. Masalahnya adalah ritmenya belum tepat.