Verifikasi merge request dengan test pyramid dan smoke test bertujuan mencari titik seimbang antara feedback cepat dan cakupan risiko. Jika semua test dijalankan di setiap merge request, pipeline sering lambat dan developer mulai mengabaikan hasil CI. Jika test terlalu sedikit, regresi lolos ke branch utama atau bahkan ke produksi.

Pendekatan yang praktis adalah membagi test berdasarkan lapisan risiko: unit test untuk logika kecil dan cepat, integration test untuk kontrak antar komponen, dan end-to-end (E2E) test untuk alur bisnis penting. Smoke test kemudian ditempatkan sebagai gerbang minimum untuk memastikan aplikasi masih bisa berjalan pada jalur kritikal sebelum merge atau deploy.

Mengapa workflow verifikasi merge request sering gagal

Banyak tim menghadapi dua masalah yang berlawanan:

  • CI terlalu lambat, karena semua test dijalankan pada setiap commit.
  • CI terlalu longgar, karena hanya menjalankan sedikit test agar cepat selesai.

Keduanya berbahaya. Pipeline yang lambat mendorong developer melakukan re-run acak, menganggap merah sebagai hal biasa, atau menunda merge sampai akhir hari. Pipeline yang terlalu ringan membuat regresi fungsional, perubahan skema database, atau masalah integrasi lolos tanpa terdeteksi.

Masalah utamanya biasanya bukan pada tool, tetapi pada desain workflow. Tim sering mencampur semua jenis test dalam satu tahap, tidak membedakan prioritas, dan tidak punya definisi jelas tentang test mana yang wajib lolos sebelum merge.

Peran test pyramid dalam verifikasi merge request

Test pyramid membantu menyusun strategi pengujian berdasarkan biaya eksekusi dan nilai deteksi bug. Bentuk piramida berarti jumlah test terbanyak ada di dasar karena paling murah dan cepat, sedangkan jumlah test paling sedikit ada di puncak karena lebih mahal dan lebih rapuh.

1. Unit test: lapisan dasar untuk feedback tercepat

Unit test memverifikasi fungsi, class, atau modul secara terisolasi. Target utamanya adalah logika bisnis, validasi, transformasi data, perhitungan, kebijakan otorisasi, atau cabang logika yang mudah rusak saat refactor.

Ciri unit test yang baik:

  • Tidak bergantung pada jaringan, file system, database, atau service eksternal.
  • Eksekusinya cepat dan deterministik.
  • Gagal karena bug logika, bukan karena lingkungan.

Di merge request, unit test biasanya wajib selalu dijalankan. Ini adalah lapisan paling efisien untuk mendeteksi regresi dini.

2. Integration test: memverifikasi kontrak antar komponen

Integration test memeriksa interaksi nyata antara beberapa komponen, misalnya:

  • API dengan database.
  • Service dengan message queue.
  • Repository dengan skema tabel.
  • Endpoint HTTP dengan autentikasi, validasi, dan persistence.

Lapisan ini penting karena banyak bug nyata muncul bukan dari logika murni, tetapi dari asumsi salah antar komponen: kolom database berubah, serialisasi tidak cocok, transaksi gagal, atau migrasi merusak query.

Dalam merge request, integration test idealnya dijalankan untuk area yang terdampak atau untuk kumpulan test inti yang mewakili risiko tinggi.

3. End-to-end test: sedikit, fokus, dan bernilai tinggi

E2E test memverifikasi sistem dari sudut pandang pengguna atau client nyata. Misalnya alur login, membuat pesanan, checkout, upload file, atau reset password. Karena menjalankan aplikasi hampir secara penuh, E2E lebih lambat, lebih mahal, dan lebih rentan flaky dibanding unit atau integration test.

Karena itu, E2E tidak boleh menjadi mayoritas test di merge request. Gunakan secukupnya untuk jalur bisnis paling kritikal.

Kapan menjalankan tiap lapisan test

Strategi yang sehat bukan menjalankan semua test di semua event, melainkan memetakan test ke momen yang tepat.

Saat developer bekerja lokal

  • Jalankan unit test terkait perubahan.
  • Jalankan integration test yang dekat dengan area yang diubah.
  • Gunakan smoke test lokal bila perubahan menyentuh startup, routing, autentikasi, atau konfigurasi.

Tujuannya adalah menangkap bug sebelum kode sampai ke CI.

Saat pull request atau merge request dibuat

  • Wajib: linting, static analysis bila ada, unit test, dan smoke test.
  • Sering wajib: subset integration test untuk komponen kritikal.
  • Opsional bergantung risiko: subset E2E untuk perubahan yang menyentuh alur pengguna utama.

Prinsipnya, merge request harus memberi feedback cepat. Jika pipeline butuh terlalu lama, pisahkan tahap cepat dan tahap mendalam.

Saat merge ke branch utama

  • Jalankan suite yang lebih lengkap.
  • Tambahkan integration test penuh bila di MR hanya menjalankan subset.
  • Jalankan E2E yang lebih luas, terutama untuk area lintas modul.

Branch utama harus dijaga tetap stabil, jadi coverage verifikasi di sini layak lebih besar daripada di MR biasa.

Sebelum deploy ke staging atau production

  • Jalankan smoke test sebagai gate.
  • Jika deploy berisiko tinggi, jalankan subset E2E yang benar-benar kritikal.
  • Verifikasi health check, konektivitas dependency utama, dan jalur transaksi inti.

Smoke test sebelum deploy berguna untuk memastikan artifact, konfigurasi, dan lingkungan target benar-benar bisa menjalankan aplikasi, bukan hanya lolos test di lingkungan build.

Menempatkan smoke test sebagai gate sebelum merge atau deploy

Smoke test adalah pengujian cepat untuk menjawab pertanyaan sederhana: apakah sistem masih hidup dan jalur paling penting masih berfungsi? Bukan untuk memeriksa semua detail, melainkan untuk mendeteksi kegagalan besar dengan biaya rendah.

Apa yang layak masuk smoke test

  • Aplikasi bisa start dan melayani request dasar.
  • Endpoint health atau readiness mengembalikan status sehat.
  • Login atau autentikasi dasar berhasil.
  • Operasi baca/tulis minimum ke database berhasil.
  • Satu alur bisnis utama berjalan dari awal sampai akhir dalam bentuk singkat.

Untuk aplikasi backend API, smoke test dapat memverifikasi startup service, autentikasi token, akses satu endpoint penting, dan satu write path sederhana. Untuk aplikasi web, smoke test dapat memverifikasi halaman utama terbuka, login berhasil, dan satu aksi inti seperti membuat entitas dasar.

Smoke test sebelum merge

Penempatan ini cocok jika:

  • Tim sering mengalami regresi di jalur kritikal meskipun unit test hijau.
  • Perubahan konfigurasi, routing, wiring dependency, atau migrasi sering bermasalah.
  • Biaya menjalankan smoke test masih rendah.

Gate ini memberi perlindungan tambahan tanpa menunggu full E2E suite.

Smoke test sebelum deploy

Penempatan ini hampir selalu masuk akal, karena lingkungan deploy dapat berbeda dari CI build. Variabel lingkungan, secret, koneksi database, cache, storage, dan service eksternal bisa menyebabkan aplikasi gagal hidup meskipun test sebelumnya lolos.

Catatan: Smoke test bukan pengganti integration test atau E2E. Smoke test adalah filter cepat untuk mendeteksi kegagalan besar lebih awal.

Strategi praktis untuk aplikasi web dan backend

Contoh pemetaan untuk backend API

Misalkan tim mengelola API pesanan.

  • Unit test: perhitungan total harga, aturan diskon, validasi status pesanan, kebijakan otorisasi.
  • Integration test: endpoint create order menulis ke database, transaksi rollback saat stok tidak cukup, event dikirim ke queue, repository membaca data sesuai filter.
  • E2E test: client login, membuat pesanan, membayar, dan melihat status akhir.
  • Smoke test: service start, health check sukses, login berhasil, create order minimal sukses.

Di merge request, jalankan seluruh unit test, integration test inti untuk order/auth/database, dan smoke test. E2E lengkap dijalankan setelah merge atau sebelum deploy.

Contoh pemetaan untuk aplikasi web

  • Unit test frontend: formatter, state reducer, helper validasi, utility permission.
  • Integration test frontend: komponen form dengan API mock atau service nyata di lingkungan uji, interaksi antar komponen penting.
  • E2E: login, navigasi ke dashboard, submit form penting, logout.
  • Smoke test: halaman utama terbuka, aset utama termuat, login bisa dilakukan, satu halaman inti bisa diakses.

Untuk frontend, salah satu sumber waktu CI yang boros adalah terlalu banyak UI test yang memverifikasi detail visual kecil. Detail seperti itu lebih cocok ditangani oleh unit/integration test komponen atau visual regression terpisah bila memang diperlukan.

Aturan memilih test kritikal untuk gate merge request

Tidak semua test layak dijadikan syarat wajib sebelum merge. Pilih berdasarkan risiko, bukan sekadar berdasarkan keberadaan test.

Kriteria pemilihan

  • Dampak bisnis tinggi: login, pembayaran, checkout, pembuatan transaksi, otorisasi.
  • Sering rusak: area yang historinya sering menghasilkan bug atau konflik antar tim.
  • Menyentuh banyak komponen: alur yang melibatkan API, database, cache, queue, atau frontend-backend.
  • Kontrak penting: serialisasi payload, skema DB, idempotensi, aturan transaksi.
  • Mudah diverifikasi cepat: test bernilai tinggi dengan waktu eksekusi pendek lebih cocok sebagai gate.

Yang sebaiknya tidak dijadikan gate utama

  • Test yang sering flaky dan belum diperbaiki.
  • Test visual atau snapshot yang sangat sensitif terhadap perubahan minor.
  • Test alur panjang yang manfaatnya kecil dibanding durasi eksekusi.

Menangani flaky test agar CI tetap dipercaya

Flaky test adalah musuh utama workflow verifikasi merge request. Jika hasil test kadang gagal tanpa perubahan kode, tim akan kehilangan kepercayaan pada pipeline.

Penyebab umum flaky test

  • Ketergantungan waktu nyata, timeout terlalu ketat, atau race condition.
  • Data uji tidak bersih antar test.
  • Berbagi resource global seperti database, file, cache, port, atau akun user.
  • Ketergantungan jaringan atau service eksternal yang tidak stabil.
  • Urutan test memengaruhi state test lain.

Praktik mengurangi flaky test

  • Isolasi test: setiap test menyiapkan dan membersihkan datanya sendiri.
  • Hindari sleep statis: gunakan polling dengan batas waktu yang jelas jika memang menunggu proses asinkron.
  • Mock seperlunya: untuk dependency eksternal yang tidak ingin diuji langsung di MR.
  • Jangan berbagi state global: hindari singleton mutable, file sementara bersama, atau database yang tidak direset.
  • Tandai dan karantina flaky test: keluarkan dari gate utama sampai akar masalah diperbaiki, jangan biarkan test acak memblokir semua merge selamanya.

Kesalahan umum: mengatasi flaky test dengan otomatis retry tanpa investigasi. Retry boleh dipakai sementara untuk menstabilkan pipeline, tetapi harus disertai tiket perbaikan yang jelas.

Isolasi data uji dan kontrol waktu

Isolasi data uji

Test yang berbagi database atau data fixture tetap sering menghasilkan kegagalan semu. Pendekatan yang lebih aman:

  • Gunakan database terpisah untuk test.
  • Reset state setiap test, misalnya dengan transaksi rollback atau recreate schema sesuai kebutuhan.
  • Buat data seminimal mungkin dan spesifik pada kebutuhan test.
  • Hindari bergantung pada data bawaan yang bisa berubah diam-diam.

Untuk integration test, gunakan seed yang kecil dan deterministik. Untuk E2E, siapkan data awal yang konsisten agar jalur kritikal selalu bisa diuji tanpa tergantung sisa eksekusi sebelumnya.

Kontrol waktu

Waktu sering menjadi sumber bug yang sulit direproduksi. Test yang menyentuh token, expiry, penjadwalan, atau sorting berbasis waktu sebaiknya tidak bergantung pada jam sistem aktual.

  • Bekukan waktu bila framework atau library pengujian mendukungnya.
  • Injeksi sumber waktu sebagai dependency, bukan memanggil waktu sistem langsung di semua tempat.
  • Gunakan zona waktu yang eksplisit dalam test.
  • Hindari assert yang terlalu ketat terhadap timestamp aktual jika tidak diperlukan.

Dengan waktu yang terkendali, test menjadi lebih deterministik dan mudah di-debug.

Contoh alur CI ringkas yang seimbang

Berikut contoh alur yang umum dipakai untuk menjaga merge request tetap cepat:

stages:
  - validate
  - test-fast
  - test-risk
  - build
  - deploy-smoke

validate:
  run:
    - lint
    - static-analysis

test-fast:
  run:
    - unit-tests
    - smoke-tests

test-risk:
  run:
    - critical-integration-tests
    - changed-area-tests

build:
  run:
    - build-artifact

deploy-smoke:
  when: before_deploy_or_post_deploy
  run:
    - deploy-to-target-env
    - health-check
    - critical-smoke-flow

Maknanya:

  • validate memberi feedback paling cepat untuk kesalahan sintaks, style, atau analisis statis.
  • test-fast menjadi gate utama MR karena cepat tetapi tetap bernilai tinggi.
  • test-risk menambah proteksi untuk area kritikal tanpa memaksa seluruh E2E berjalan tiap saat.
  • deploy-smoke memastikan aplikasi yang sudah dibangun benar-benar hidup di lingkungan target.

Bila infrastruktur mendukung, tahap-tahap ini bisa dijalankan paralel untuk mempercepat total waktu tunggu.

Review failure: jangan sampai hasil merah di-skip

Workflow yang baik tidak berhenti pada menjalankan test. Tim juga perlu disiplin meninjau kegagalan. Salah satu anti-pattern paling mahal adalah menganggap failure sebagai noise rutin.

Aturan praktis review failure

  • Setiap failure pada gate utama harus punya pemilik penanganan yang jelas.
  • Bedakan failure produk, failure test, dan failure lingkungan CI.
  • Jika test dimatikan sementara, harus ada alasan, tiket, dan tenggat evaluasi.
  • Jangan merge dengan status merah hanya karena "sepertinya flaky" tanpa bukti.

Klasifikasi cepat saat pipeline gagal

  1. Apakah reproducible secara lokal? Jika ya, kemungkinan bug produk atau test yang deterministik.
  2. Apakah gagal di area yang tidak berubah? Bisa jadi dependency bersama, data uji bocor, atau flaky test lama.
  3. Apakah log menunjukkan timeout atau koneksi? Periksa resource CI, service dependency, dan strategi wait.
  4. Apakah assertion terlalu rapuh? Periksa coupling ke detail implementasi yang tidak relevan.

Dengan klasifikasi seperti ini, tim mengurangi kecenderungan untuk sekadar menekan tombol re-run tanpa belajar dari kegagalan.

Trade-off biaya vs kecepatan feedback

Tidak ada konfigurasi tunggal yang sempurna untuk semua tim. Semakin banyak test di merge request, semakin besar kemungkinan regresi cepat tertangkap, tetapi semakin lama feedback kembali ke developer.

Saat memilih pipeline lebih cepat

Cocok jika:

  • Tim kecil dengan frekuensi commit tinggi.
  • Area perubahan relatif terlokalisasi.
  • Unit dan integration test sudah kuat.

Konsekuensinya, sebagian bug lintas sistem mungkin baru tertangkap setelah merge atau sebelum deploy.

Saat memilih pipeline lebih ketat

Cocok jika:

  • Aplikasi menyangkut transaksi penting atau risiko bisnis tinggi.
  • Perubahan sering menyentuh banyak komponen sekaligus.
  • Tim pernah mengalami insiden produksi karena celah verifikasi.

Konsekuensinya adalah waktu tunggu lebih panjang dan biaya infrastruktur CI meningkat.

Pilihan terbaik biasanya bukan salah satu ekstrem, melainkan dua lapis verifikasi: gate MR yang cepat dan branch/deploy verification yang lebih lengkap.

Anti-pattern umum dalam verifikasi merge request

  • Semua test adalah E2E: mahal, lambat, dan sulit di-debug.
  • Unit test terlalu sedikit: logika sederhana baru ketahuan rusak di integration atau E2E.
  • Smoke test terlalu banyak: jika smoke test berubah menjadi suite besar, ia kehilangan tujuan utamanya sebagai pengecekan cepat.
  • Mengandalkan mocking berlebihan: integration bug nyata tidak pernah terlihat.
  • Gate diisi test rapuh: pipeline sering merah karena detail yang tidak penting.
  • Test data bersama: test saling memengaruhi dan hasil jadi tidak stabil.
  • Failure culture buruk: test merah dianggap normal sehingga sinyal penting hilang.

Checklist implementasi bertahap untuk tim kecil

Jika tim belum punya workflow yang rapi, jangan langsung membangun sistem yang terlalu kompleks. Mulai bertahap.

Tahap 1: buat gate minimum yang bisa dipercaya

  1. Pastikan linting dan unit test berjalan otomatis di setiap MR.
  2. Pilih 3-5 smoke test untuk jalur bisnis paling kritikal.
  3. Tetapkan bahwa MR tidak boleh di-merge jika gate minimum gagal.

Tahap 2: tambahkan integration test inti

  1. Identifikasi area yang sering rusak: auth, database write path, pembayaran, sinkronisasi data.
  2. Buat integration test untuk kontrak penting, bukan semua skenario kecil.
  3. Masukkan subset integration test itu ke MR gate bila durasinya masih masuk akal.

Tahap 3: pisahkan suite cepat dan suite lengkap

  1. Labeli test berdasarkan kategori: unit, integration, smoke, e2e.
  2. Jalankan suite cepat pada MR.
  3. Jalankan suite lengkap pada branch utama, nightly build, atau sebelum deploy.

Tahap 4: stabilkan pipeline

  1. Catat flaky test dan karantina bila perlu.
  2. Perbaiki isolasi data uji dan kontrol waktu.
  3. Tinjau log failure secara rutin untuk menemukan pola kegagalan berulang.

Tahap 5: optimalkan berdasarkan data

  1. Periksa test mana yang paling sering menemukan bug.
  2. Hapus atau turunkan prioritas test mahal yang jarang memberi nilai.
  3. Tambah coverage di area yang sering lolos dari gate tetapi gagal setelah merge.

Penutup

Verifikasi merge request dengan test pyramid dan smoke test bukan soal menambah sebanyak mungkin test, tetapi menempatkan test yang tepat di waktu yang tepat. Unit test memberi feedback tercepat, integration test menjaga kontrak antar komponen, E2E melindungi alur bisnis penting, dan smoke test menjadi pagar cepat sebelum merge atau deploy.

Jika tujuan tim adalah mendeteksi regresi tanpa membuat CI lambat, fokuslah pada tiga hal: lapisan test yang jelas, gate minimum yang bernilai tinggi, dan pipeline yang bisa dipercaya. Dari sana, workflow dapat dikembangkan bertahap sesuai risiko aplikasi dan kapasitas tim.