Flaky testy a AI: automatická klasifikace za sekundu
V průměrné Cypress suite je 8–20 % testů flaky. Když vám spadne build, stojíte před otázkou: je to skutečný bug, nebo jen zase flake? Ruční triage zabere 10–15 minut na jedno selhání. AI to zvládne za sekundu.
Tenhle článek je hloubkový průchod stavbou automatického klasifikátoru — od definice flakiness přes trénovací data po integraci do CI.
Co přesně je flaky test
Test je flaky, když se při stejném vstupu chová nedeterministicky. Zdroje flakiness:
- Race conditions — DOM se vykresluje asynchronně, test byl rychlejší než UI.
- Síťová flakiness — API třetí strany mělo timeout a test spadl.
- Sdílený stav — předchozí test nechal databázi v jiném stavu.
- Prostředí — CI runner byl přetížený a animace trvala déle.
- Nedeterministická data — test závisí na aktuálním datu nebo náhodném ID.
Klasifikátor: signály, které AI využívá
Když test selže, máte k dispozici tyhle signály:
- Chybová hláška —
Timed out retrying after 4000msvs.expected 'ACTIVE' to equal 'PENDING'. První je nejspíš flake, druhé nejspíš reálný bug. - Stack trace — selhání v
cy.wait()vs. selhání vcy.contains(). - Historická stabilita — spadl tenhle test za posledních 30 buildů 12×? Flake.
- Souvisí commit s testovanou oblastí? — pokud diff mění
checkout.tsxa selhal checkout test, je to nejspíš reálný bug. - Screenshot a video — pokud máte vizuální AI, porovnejte stav před a po. Když se UI změnilo, půjde nejspíš o commit s novou funkcí.
Architektura klasifikátoru
Pro náš produkční klasifikátor jsme použili tenhle stack:
Zdroje dát: - Cypress Dashboard API (história behu) - Git commit hash + diff - JUnit XML výstup - Screenshots + video Feature extraction (Python): - error_message_category (ML klasifikácia z textu) - test_stability_last_30d (historical ratio) - files_changed_relevance (Jaccard similarity) - timing_anomaly (z-score behu voči priemeru) Model: - Gradient Boosting (XGBoost) - Tréningová množina: ~8000 manuálne označených failov - Accuracy: 88% / Precision flake: 92% Inference: - REST endpoint (AWS Lambda) - Latency: ~120ms p50 - Invoked z Jenkins post-build step
Jak data olabelovat (to je nejtěžší)
Trénovací data jsou nejkritičtější krok. Dvě ověřené cesty:
- Historická ruční triage — pokud máte dva a víc let záznamů z Cypress Dashboardu, dobré QA týmy failnuté testy často tagovaly jako „flaky" nebo „bug". Export a vyčištění = dataset.
- Labelování dopředu přes opakování — každé selhání automaticky zopakujte 3×. Když 2 ze 3 projdou, label = flaky. Když spadnou všechny tři, label = reálný bug. Po 4–6 týdnech máte zhruba 500 olabelovaných vzorků.
Integrace do CI
Náš hook v Jenkinsfile:
post {
failure {
script {
def failedTests = readJSON file: 'cypress/reports/junit.json'
def response = httpRequest(
url: 'https://classifier.internal/classify',
httpMode: 'POST',
contentType: 'APPLICATION_JSON',
requestBody: groovy.json.JsonOutput.toJson([
failed_tests: failedTests,
commit_sha: env.GIT_COMMIT,
build_id: env.BUILD_NUMBER,
])
)
def results = readJSON text: response.content
results.each { r ->
if (r.classification == 'flake' && r.confidence > 0.85) {
// Auto-retry, no human needed
sh "npx cypress run --spec '${r.test_file}'"
} else {
// Create Jira ticket
slackSend(channel: '#qa-alerts', message: "🐛 Real bug: ${r.test_name}")
}
}
}
}
}
Výsledky u klienta (6 měsíců)
- Čas ruční triage klesl z 12 minut na jedno selhání na 1,5 minuty (jen review klasifikátoru).
- False positive rate (klasifikátor řekl bug, byl to flake): 4 %.
- False negative rate (klasifikátor řekl flake, byl to bug): 7 % — tohle je kritické a je potřeba to minimalizovat.
- Ušetřený čas QA týmu: ~35 hodin měsíčně.
Kdy vlastní klasifikátor nestavět
Pokud máte méně než 200 Cypress testů a flakiness pod 10 %, ROI je nulové. Doporučujeme:
- Řešit flakiness na úrovni kódu testů (lepší waity, intercepty).
- Používat Cypress Cloud (pokud ho platíte) — jeho detekce „Flaky Test" je bez ML, ale malému týmu stačí.
Chcete stejný přístup u vás? Napište nám — domluvíme 30minutový discovery call.