Pendahuluan

Strategi Test Otomatis Rust untuk Deteksi Flaky dan Regresi menuntut pendekatan yang sistematis: bukan hanya menjalankan semua test, tapi memastikan test stabil dan berguna untuk menangkap regresi. Dalam 1-2 paragraf ini, fokus langsung ke bagaimana kita menjaga cakupan test tetap andal dan dapat dijalankan baik secara lokal maupun di pipeline CI.

Masing-masing tingkatan test membutuhkan perlakuan berbeda, sementara tooling seperti cargo nextest, tokio::test, dan snapshot testing membantu menstabilkan hasil yang berpotensi flaky.

Leveling Pengujian untuk Stabilitas

Unit Test: Kecepatan dan Isolasi

Unit test harus cepat, deterministik, dan tidak tergantung I/O eksternal. Gunakan pola dependency injection, mock trait, dan cargo features untuk memisahkan logika dari sumber daya nyata.

Contoh praktik: gunakan modul test di bawah #[cfg(test)] untuk menyediakan stub Store. Pastikan test tidak membaca file sistem secara langsung dan konsisten menginisialisasi state sebelum tiap test.

Integration Test: Lingkup Lebih Luas dengan Kontrol

Pengujian integrasi menggabungkan beberapa komponen. Di Rust, letakkan file di tests/ dan jalankan dengan cargo test --test nama. Untuk menghindari flaky, kontrol environment dengan test harness yang membangun database sementara (misalnya menggunakan container ringan) dan membersihkan state setiap test.

Gunakan std::sync::OnceLock atau shared setup untuk mengurangi duplikasi waktu startup tanpa mengorbankan isolasi.

Verification Test: Simulasi Real-World dan Snapshot

Verification test (sering disebut end-to-end dalam konteks backend) memerlukan data mirip produksi. Terapkan snapshot testing untuk membandingkan keluaran terstruktur tanpa mengandalkan string mentah. Perhatikan potensi flaky akibat timestamp atau ID acak; gunakan generator deterministic atau filter data sensitif.

Strategi lain: jalankan orchestration environment terotomat, lalu bandingkan hasil dengan baseline yang dikontrol.

Tooling dan Feature Relevan

Cargo Nextest untuk Paralel Stabil

Cargo nextest menambahkan layer deterministik dengan grouping dan retries terbatas. Berikut contoh konfigurasi dasar di Nextest.toml:

[profile.default]
retry = 2
partition = "balanced"

Konfigurasi ini memastikan test yang gagal karena race condition dapat dicoba ulang tanpa membanjiri build log. Hindari retry berlebihan; lebih baik investigasi test yang selalu gagal.

Tokio::test dan Simulasi Async

Untuk test async, #[tokio::test(flavor = "multi_thread", worker_threads = 2)] menyediakan runtime yang mirip produksi. Pastikan test menunggu semua task selesai (gunakan tokio::time::timeout untuk menghindari hang) dan tidak mengandalkan sleep fixed yang mudah flaky.

Snapshot Testing dan Simulasi I/O

Gunakan crate seperti insta untuk snapshot. Simulasi I/O dilakukan dengan tokio-test atau stub trait file. Perbarui snapshot hanya setelah memastikan behavior memang berubah, dan simpan file snapshot dalam kontrol versi agar regression dapat dideteksi.

Workflow Verifikasi Lokal ke CI

1. Isolasi Lingkungan

Pastikan local dev memakai container, docker compose, atau profile cargo yang sama dengan CI. Alat seperti cargo make atau direnv menjaga env konsisten.

2. Stabilisasi Timing dan I/O

Ubah test flaky akibat timing dengan parameterizing waktu atau menggunakan tokio::time::pause saat simulasi. Hindari sleep(Duration::from_millis(...)) tanpa alasan kuat.

3. Retries Terbatas

Gunakan retry hanya di level tooling (contoh cargo nextest --retry 2) atau CI. Terapkan batas global agar team tidak menyalahgunakan retry sebagai pelapis bug.

4. Logging dan Observabilitas

Aktifkan log level yang memadai saat test berjalan di CI. Gunakan RUST_LOG atau tracing subscriber dengan sink file agar kegagalan bisa dianalisis.

5. Gating Merge

Jalankan step test pada push dan PR, lalu konfigurasikan branch protection rule untuk mencegah merge jika test gagal. Pastikan pipeline memicu versi cargo test yang sama di lokal dan CI.

Checklist Praktis dan Contoh Konfigurasi

  • Definisikan leveling test (unit, integration, verification) dan dokumentasikan area coverage.
  • Gunakan cargo nextest dengan retry terbatas dan kategori untuk mengelompokkan test yang kerap flaky.
  • Pastikan test async memakai tokio::test dengan timeout dan runtime yang konsisten.
  • Snapshot testing simpan dalam kontrol versi; perbarui hanya setelah perubahan behavior diverifikasi.
  • CI pipeline mengandung step lokal yang sama (rustfmt, clippy, cargo nextest) dan memblokir merge jika gagal.
  • Monitoring log CI disimpan sebagai artifact untuk debugging test flaky.

Contoh Nextest.toml ringkas:

[profile.default]
partition = "balanced"
retry = 1
report = "json"

Contoh .github/workflows/test.yaml:

name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up Rust
        uses: actions/setup-rust@v1
      - name: Install Nextest
        run: cargo install cargo-nextest
      - name: Run Tests
        run: cargo nextest run --workspace

Dengan checklist ini, tim memiliki panduan implementasi dan pipeline yang menjaga pengujian tetap stabil, mendeteksi flaky test, dan mencegah regresi.