Strategi visualisasi test suite untuk menemukan flaky test bukan sekadar membuat dashboard yang menarik. Tujuan utamanya adalah mengubah hasil test yang tersebar di log CI menjadi peta hubungan yang bisa ditelusuri: test mana yang sering gagal, modul apa yang paling terdampak, fixture atau mock mana yang menjadi sumber ketidakstabilan, dan jalur pipeline mana yang paling sering menjadi bottleneck.
Jika flaky test hanya dilihat sebagai daftar gagal/lolos per build, akar masalahnya sering tidak terlihat. Dengan pendekatan visual eksploratif—terinspirasi dari cara alat seperti WikiSpy menampilkan relasi antar entitas—kita bisa menerjemahkannya ke konteks engineering: relasi file test, module, fixture, service mock, job CI, environment, dan riwayat perubahan. Hasilnya bukan hanya identifikasi flaky test, tetapi juga prioritas perbaikan dan guardrail agar regresi tidak lolos.
Mengapa flaky test sulit ditangani tanpa visualisasi
Flaky test adalah test yang kadang gagal dan kadang lolos tanpa perubahan fungsional yang jelas pada kode yang diuji. Masalah utamanya bukan hanya noise pada CI, tetapi juga hilangnya kepercayaan terhadap test suite. Saat tim mulai menganggap kegagalan test sebagai "mungkin hanya flake", regresi nyata lebih mudah lolos.
Tanpa visualisasi, data flaky test biasanya tersebar dalam beberapa tempat:
- log CI per job,
- laporan unit/integration test,
- history rerun manual,
- catatan perubahan kode,
- observasi informal dari tim.
Data tersebut cukup untuk investigasi manual satu kasus, tetapi buruk untuk melihat pola. Visualisasi membantu menjawab pertanyaan yang lebih penting:
- Apakah test yang gagal saling terhubung lewat fixture atau service mock yang sama?
- Apakah flaky test terkonsentrasi pada satu modul tertentu?
- Apakah kegagalan meningkat hanya di runner atau job tertentu?
- Apakah bottleneck verifikasi muncul dari subset test yang lambat dan sering di-rerun?
- Apakah ada area regresi yang selalu ikut terdampak setelah perubahan di komponen tertentu?
Model mental: peta relasi, bukan sekadar daftar test
Cara paling berguna untuk memvisualisasikan test suite adalah menganggapnya sebagai graf. Setiap simpul merepresentasikan entitas engineering, dan setiap sisi merepresentasikan relasi yang relevan untuk diagnosis.
Jenis simpul yang berguna
- Test case: identifier unik untuk satu test.
- Test file: file yang berisi beberapa test.
- Module atau package: area kode yang diuji.
- Fixture: setup bersama, data seed, temporary resource.
- Service mock: dependency eksternal yang disimulasikan.
- CI job: jalur eksekusi test di pipeline.
- Environment: OS, runtime, container image, database backend, atau konfigurasi penting lain.
- Commit atau PR: sumber perubahan yang memicu build.
Jenis sisi yang berguna
- TEST_USES_FIXTURE: test menggunakan fixture tertentu.
- TEST_MOCKS_SERVICE: test bergantung pada mock service tertentu.
- TEST_COVERS_MODULE: test menguji modul tertentu.
- TEST_RUNS_IN_JOB: test dieksekusi di job CI tertentu.
- TEST_FAILED_IN_BUILD: test gagal di build tertentu.
- COMMIT_TOUCHED_MODULE: commit mengubah modul tertentu.
- BUILD_TRIGGERED_BY_COMMIT: build dipicu oleh commit/PR tertentu.
Dengan model ini, Anda tidak hanya melihat bahwa Test A gagal tiga kali minggu ini. Anda bisa melihat bahwa Test A, Test B, dan Test C semua menggunakan fixture yang sama, berjalan di job yang sama, dan mulai bermasalah setelah perubahan pada modul tertentu. Itulah nilai utama visualisasi graf.
Data yang perlu dikumpulkan
Visualisasi yang berguna dimulai dari data yang cukup kaya. Jika hanya menyimpan status pass/fail, kemampuan analisis akan sangat terbatas.
Data minimum per eksekusi test
- test_id: identifier stabil, misalnya kombinasi suite, file, dan nama test.
- file_path: lokasi file test.
- suite: unit, integration, e2e, contract, dan sebagainya.
- status: passed, failed, skipped, timeout.
- duration_ms: durasi eksekusi.
- error_signature: ringkasan error yang sudah dinormalisasi.
- attempt: percobaan ke berapa, jika ada retry/rerun.
- build_id: build atau pipeline run tempat test dijalankan.
- job_id: job atau shard tempat test dieksekusi.
- commit_sha atau change_id: sumber perubahan.
- runner_fingerprint: identitas umum environment eksekusi.
- timestamp: waktu eksekusi.
Data relasi yang sangat membantu
- Daftar fixture yang digunakan test.
- Daftar mock atau dependency eksternal yang disentuh.
- Module atau file produksi yang dicakup secara statis atau dari metadata kepemilikan.
- Sharding info bila test suite dibagi ke beberapa worker.
- Riwayat retry otomatis dan hasilnya.
- Kategori kegagalan: assertion, timeout, network, resource lock, snapshot mismatch, environment startup.
Sinyal flaky test yang perlu dihitung
Flaky test tidak selalu berarti "gagal beberapa kali". Anda perlu beberapa sinyal sekaligus.
- Pass setelah retry: test gagal di percobaan awal lalu lolos saat rerun tanpa perubahan kode.
- Status bolak-balik: pola pass-fail-pass atau fail-pass-fail pada commit yang berdekatan.
- Error signature bervariasi: test yang sama gagal dengan pesan berbeda-beda.
- Durasi sangat menyebar: variasi waktu eksekusi yang terlalu besar sering menandakan race condition atau resource contention.
- Terkait environment tertentu: gagal hanya pada satu job, shard, atau runner class.
- Cluster dependence: beberapa test sering gagal bersama karena fixture atau service mock bersama.
Jangan langsung menyimpulkan semua test yang pernah pass setelah retry sebagai flaky. Retry bisa menyamarkan bug nyata. Label flaky sebaiknya berasal dari kombinasi pola, bukan satu sinyal tunggal.
Skema data sederhana untuk analisis
Anda tidak perlu langsung memakai graph database. Untuk tahap awal, tabel relasional atau dokumen JSON sudah cukup, selama relasinya dapat dibentuk kembali saat query atau proses agregasi.
Contoh skema event test
{
"build_id": "build-2026-08-18-1024",
"job_id": "test-linux-shard-3",
"commit_sha": "abc123def456",
"branch": "main",
"timestamp": "2026-08-18T10:24:33Z",
"test_results": [
{
"test_id": "integration/user_login::should_lock_after_retries",
"file_path": "tests/integration/user_login.test",
"suite": "integration",
"status": "failed",
"attempt": 1,
"duration_ms": 1842,
"error_signature": "timeout_waiting_for_lock",
"fixtures": ["db_seed_users", "redis_session_fixture"],
"mocks": ["email_service_mock"],
"modules": ["auth/login", "auth/lockout"]
},
{
"test_id": "integration/user_login::should_lock_after_retries",
"file_path": "tests/integration/user_login.test",
"suite": "integration",
"status": "passed",
"attempt": 2,
"duration_ms": 1310,
"error_signature": null,
"fixtures": ["db_seed_users", "redis_session_fixture"],
"mocks": ["email_service_mock"],
"modules": ["auth/login", "auth/lockout"]
}
]
}Contoh tabel logis
- test_execution: satu baris per test per attempt.
- test_metadata: test_id, file_path, suite, owner, tags.
- test_fixture_edge: test_id ke fixture_name.
- test_module_edge: test_id ke module_name.
- build_metadata: build_id, branch, commit_sha, trigger_type, runner_class.
- failure_cluster: hasil agregasi untuk kelompok error atau korelasi kegagalan.
Skema ini cukup untuk membangun dashboard, graf, dan aturan triase tanpa ketergantungan pada tool tertentu.
Strategi visualisasi test suite yang benar-benar berguna
Visualisasi terbaik bukan yang paling kompleks, tetapi yang paling cepat membantu pengambilan keputusan. Mulailah dari tiga tampilan utama.
1. Graf relasi untuk eksplorasi akar masalah
Gunakan graf interaktif dengan simpul test, fixture, mock, module, dan CI job. Fokus utamanya adalah mencari cluster.
Skenario yang ingin terlihat jelas:
- Satu fixture terhubung ke banyak flaky test.
- Satu job CI mengumpulkan banyak kegagalan lintas modul.
- Satu module memiliki kombinasi durasi tinggi dan failure rate tinggi.
- Satu mock service menjadi pusat banyak timeout.
Aturan visual yang biasanya efektif:
- Ukuran node test merepresentasikan frekuensi gagal.
- Warna node test merepresentasikan tingkat kecurigaan flaky.
- Ketebalan edge merepresentasikan kekuatan hubungan, misalnya jumlah kegagalan bersama.
- Warna edge bisa membedakan relasi struktural dan relasi observasional.
2. Heatmap stabilitas vs durasi
Graf bagus untuk eksplorasi, tetapi heatmap atau scatter plot sering lebih cepat untuk prioritas. Letakkan:
- sumbu X: median durasi atau persentil tinggi durasi,
- sumbu Y: failure rate atau flaky score,
- warna: suite atau module,
- ukuran titik: frekuensi eksekusi.
Dari sini Anda bisa langsung melihat test yang sekaligus sering gagal dan mahal dijalankan. Itulah kandidat bottleneck verifikasi yang sebaiknya diprioritaskan.
3. Timeline perubahan stabilitas
Tambahkan tampilan waktu untuk menjawab dua pertanyaan:
- Kapan flaky behavior mulai muncul?
- Apakah ada korelasi dengan perubahan kode, image runner, atau konfigurasi pipeline?
Timeline minimal menampilkan:
- tren failure rate per test,
- tren retry-pass rate,
- event perubahan infrastruktur CI,
- deploy perubahan shared fixture atau helper test.
Contoh perhitungan skor flaky dan korelasi cluster
Anda tidak memerlukan model statistik rumit pada fase awal. Gunakan heuristik yang transparan agar mudah dipercaya tim.
Pseudocode skor flaky
for each test_id in tests:
runs = get_recent_runs(test_id, window=30_days)
total = count(runs)
failures = count(runs where status == "failed")
retry_passes = count(groups where first_attempt_failed and later_attempt_passed)
distinct_errors = count_unique(normalize(error_signature))
duration_variance = variance(duration_ms over passed_and_failed_runs)
env_skew = concentration_of_failures_by(job_id or runner_fingerprint)
flaky_score = 0
flaky_score += weight_failure_rate(failures / max(total, 1))
flaky_score += weight_retry_pass(retry_passes / max(total, 1))
flaky_score += weight_error_variation(distinct_errors)
flaky_score += weight_duration_spread(duration_variance)
flaky_score += weight_environment_skew(env_skew)
save_flaky_score(test_id, flaky_score)Poin pentingnya bukan rumus tepatnya, tetapi sifatnya yang dapat dijelaskan. Jika tim tidak paham kenapa sebuah test diberi label berisiko, mereka cenderung mengabaikannya.
Pseudocode untuk mendeteksi cluster kegagalan
for each build in recent_builds:
failed_tests = get_failed_tests(build)
for each pair (a, b) in combinations(failed_tests, 2):
increment_cofailure(a, b)
for each pair (a, b):
score = cofailure_count(a, b) / min(total_failures(a), total_failures(b))
if score > threshold:
create_observed_edge(a, b, type="CO_FAILS_WITH", weight=score)Kemudian, overlay pasangan tersebut dengan relasi struktural seperti fixture bersama atau module bersama. Jika dua test sering gagal bersama dan berbagi fixture yang sama, sinyal akar masalahnya jauh lebih kuat.
Desain dashboard sederhana yang cukup untuk mulai
Dashboard tidak harus besar. Yang penting, engineer bisa menjawab pertanyaan operasional dalam beberapa menit.
Panel inti yang disarankan
- Top flaky tests
Daftar test dengan flaky score tertinggi, lengkap dengan owner, suite, module, dan tren 7-30 hari. - Failure clusters
Kelompok test yang sering gagal bersama, disertai fixture/mock/module yang menjadi irisan bersama. - Regression hotspots
Area module yang baru saja disentuh dan mengalami kenaikan failure rate setelah perubahan tertentu. - Verification bottlenecks
Test atau suite dengan durasi tinggi, rerun tinggi, dan kontribusi besar terhadap waktu pipeline. - Environment skew
Perbandingan kegagalan per job, runner class, OS, shard, atau konfigurasi backend.
Filter yang wajib ada
- rentang waktu,
- branch atau environment,
- suite,
- owner team,
- module,
- job CI,
- status setelah retry.
Tanpa filter ini, dashboard cepat menjadi sekadar layar statistik. Dengan filter yang tepat, engineer bisa membedakan masalah yang sistemik dari kasus lokal.
Workflow triase: dari sinyal ke tindakan
Visualisasi hanya berguna jika dihubungkan ke workflow triase yang disiplin. Berikut alur yang praktis.
Langkah 1: Bedakan flaky test dari regresi nyata
Gunakan tiga pemeriksaan awal:
- Apakah test lolos saat rerun tanpa perubahan?
- Apakah test lain yang terkait modul yang sama juga mulai gagal?
- Apakah kegagalan terkonsentrasi di satu environment?
Jika banyak test terkait modul yang sama gagal setelah perubahan baru, perlakukan dulu sebagai regresi nyata. Jangan buru-buru memberi label flaky.
Langkah 2: Telusuri pusat relasi
Buka graf dan cari node pusat: fixture, mock, atau CI job yang menjadi perantara banyak kegagalan. Node pusat sering lebih layak diperbaiki dulu daripada memperbaiki test satu per satu.
Langkah 3: Kelompokkan akar masalah
Klasifikasikan penyebab agar tindakan perbaikannya konsisten:
- State leak: test saling memengaruhi lewat database, cache, file system, atau singleton.
- Race condition: asumsi urutan event tidak stabil.
- Timeout rapuh: batas waktu terlalu agresif atau menunggu sinyal yang salah.
- Dependency eksternal: mock kurang deterministik atau service sandbox tidak stabil.
- Environment drift: perilaku berbeda antar runner, shard, atau image.
- Data fixture rapuh: setup bersama terlalu kompleks dan mudah tercemar.
Langkah 4: Pilih prioritas perbaikan
Urutkan berdasarkan gabungan dampak dan biaya:
- Seberapa sering test memblokir merge atau release.
- Seberapa banyak test lain terhubung ke akar masalah yang sama.
- Seberapa mahal dampaknya pada durasi pipeline.
- Apakah area itu dekat dengan jalur bisnis atau keamanan yang sensitif.
Perbaikan fixture bersama yang dipakai 40 test biasanya lebih bernilai daripada memperbaiki satu flaky test terisolasi.
Guardrail agar regresi tidak lolos
Salah satu risiko terbesar adalah retry dan quarantine membuat pipeline tampak hijau padahal kualitas menurun. Karena itu, visualisasi harus diiringi guardrail yang jelas.
Guardrail yang disarankan
- Retry tidak menghapus sinyal: simpan kegagalan percobaan awal sebagai event penting, walaupun build akhirnya hijau.
- Batas quarantine: test yang dikarantina harus punya owner, alasan, dan tenggat evaluasi.
- Threshold peningkatan flaky score: jika skor naik tajam pada module kritis, buat alert walau pipeline lulus.
- Blok merge untuk regresi stabil: jika test gagal konsisten tanpa pola flaky, jangan dikaburkan oleh kebijakan retry.
- Review perubahan shared test utilities: fixture, helper, dan mock global sebaiknya dianggap area berisiko tinggi.
- Sampling rerun terkontrol: rerun semua test secara agresif bisa menutupi sinyal. Gunakan rerun sebagai instrumen observasi, bukan solusi permanen.
Kesalahan umum adalah menganggap quarantine sebagai penyelesaian. Quarantine hanya memindahkan risiko. Tanpa expiry date dan pelacakan visual, daftar test yang diabaikan akan tumbuh diam-diam.
Implementasi bertahap di CI tanpa bergantung tool tertentu
Pendekatan terbaik adalah bertahap. Jangan mulai dari visualisasi canggih sebelum data dasar konsisten.
Tahap 1: Ekspor hasil test terstruktur
Pastikan setiap job CI menghasilkan artefak terstruktur, misalnya JSON atau format lain yang mudah diproses. Fokus awal:
- test_id, status, duration, attempt, build_id, job_id, timestamp, error_signature.
- Jika memungkinkan, sertakan metadata fixture, mock, dan module dari anotasi atau mapping internal.
Tahap 2: Normalisasi dan agregasi
Buat langkah pasca-test di CI untuk menggabungkan semua artefak dari shard atau job berbeda ke satu dataset build-level. Lalu:
- normalisasi error signature,
- deduplikasi test_id,
- hitung retry-pass rate, failure rate, duration spread,
- simpan hasilnya di storage yang bisa di-query.
Tahap 3: Bangun edge relasi
Relasi bisa berasal dari beberapa sumber:
- statis: mapping test ke module, fixture, mock, owner,
- dinamis: kegagalan bersama dalam build yang sama,
- operasional: test dijalankan di job atau runner tertentu.
Pada tahap ini Anda sudah bisa membuat graf sederhana.
Tahap 4: Tampilkan dashboard minimum viable
Mulailah dari tabel dan heatmap dulu. Graf interaktif bisa menyusul. Banyak tim gagal karena langsung membangun visualisasi kompleks sebelum metrik dasarnya dipercaya.
Tahap 5: Hubungkan ke triase dan alert
Tambahkan aturan operasional:
- buat issue otomatis untuk test di atas skor tertentu,
- assign owner berdasarkan module atau test path,
- alert jika ada cluster baru setelah perubahan infrastruktur CI,
- review mingguan untuk quarantine dan hotspot.
Contoh pseudocode alur CI pasca-test
collect_artifacts_from_all_jobs()
results = parse_test_artifacts()
normalized = normalize_results(results)
for each record in normalized:
enrich_with_metadata(record)
save_test_execution(record)
edges = []
edges += build_fixture_edges(normalized)
edges += build_module_edges(normalized)
edges += build_mock_edges(normalized)
edges += build_job_edges(normalized)
edges += build_cofailure_edges(normalized)
save_edges(edges)
scores = compute_flaky_scores(normalized, historical_data=last_30_days)
save_scores(scores)
publish_dashboard_data()
trigger_alerts_if_needed(scores, edges)Kesalahan yang sering terjadi
- Terlalu fokus pada satu test
Padahal sumber masalahnya adalah fixture bersama atau konfigurasi runner. - Mengandalkan retry sebagai indikator tunggal
Retry bisa membantu observasi, tetapi bukan definisi universal flaky. - Tidak menormalisasi error
Pesan error mentah sering terlalu bervariasi untuk dikelompokkan. - Tidak menyimpan metadata environment
Tanpa job_id atau runner fingerprint, skew environment sulit dibuktikan. - Metrik tanpa tindak lanjut
Dashboard yang tidak terhubung ke owner dan proses triase akan cepat diabaikan.
Tips debugging setelah hotspot ditemukan
Setelah visualisasi mengarah ke kandidat sumber masalah, investigasi teknisnya tetap harus rapi.
- Jalankan test target berulang kali dalam isolasi dan dalam urutan acak untuk mendeteksi state leak.
- Bandingkan environment yang stabil vs tidak stabil untuk mencari perbedaan konfigurasi.
- Nonaktifkan paralelisme sementara untuk menguji hipotesis race condition atau resource contention.
- Perkecil fixture bersama; pindahkan setup ke scope yang lebih sempit jika memungkinkan.
- Ganti timeout berbasis tidur tetap dengan sinkronisasi yang berbasis kondisi atau event yang deterministik.
- Audit mock service yang menyimpan state global atau punya waktu respons acak.
Penutup
Strategi visualisasi test suite untuk menemukan flaky test paling efektif ketika diperlakukan sebagai alat diagnosis sistem, bukan sekadar laporan pass/fail. Dengan memetakan relasi antara test, module, fixture, service mock, dan pipeline CI, tim bisa melihat pola yang biasanya tersembunyi di balik log yang panjang: cluster kegagalan, area regresi, dan bottleneck verifikasi.
Mulailah dari data yang kecil tetapi konsisten, bentuk relasi yang paling relevan, lalu bangun dashboard sederhana yang langsung mendukung triase. Jika dilakukan bertahap, pendekatan ini membantu tim mengurangi noise, mempercepat investigasi, dan menjaga agar retry atau quarantine tidak menutupi regresi yang sebenarnya.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!