Flaky Tests sind automatisierte Tests, die nicht wiederholbar sind: Bei unverändertem Code bestehen sie mal und schlagen mal fehl. Damit zerstören sie das Vertrauen in die CI-Pipeline (Continuous Integration), denn niemand kann mehr sagen, ob ein roter Build auf einen echten Fehler hinweist. Die zwei häufigsten Ursachen sind konkurrierende Zugriffe auf gemeinsam genutzte Daten und veränderlicher gemeinsamer Zustand.
Das Wichtigste in Kürze
- Ein Flaky Test verletzt die Grundeigenschaft der Wiederholbarkeit. Sobald Entwicklerinnen und Entwickler den CI-Ergebnissen nicht mehr trauen, geht der Nutzen von Continuous Integration verloren.
- Flaky Tests decken manchmal echte Produktionsrisiken auf: Kurze Netzwerkfehler, nicht erreichbare Abhängigkeiten und Race Conditions im Test treffen später auch die Nutzerinnen und Nutzer.
- Einen Flaky Test aus den regulären CI-Läufen zu nehmen, ist nötig, reicht aber nicht. Die Verantwortung gehört einer namentlich benannten Person, nicht einem Team.
- Meta lässt jeden neuen Test vor der Aufnahme in die CI über Nacht hundertmal parallel laufen. Diese automatisierte Eingangskontrolle hält Flaky Tests aus dem Build heraus.
- Einen Flaky Test zu löschen, ist eine legitime Lösung, vor allem wenn kleinere Tests dasselbe Risiko schon abdecken. Änderungen schnell und sicher auszuliefern, zählt mehr, als Testcode um jeden Preis zu behalten.
Was Flaky Tests sind und woran du sie erkennst
Ein unzuverlässiger Test, im Englischen Flaky Test, liefert bei gleichem Code nicht immer dasselbe Ergebnis. Führst du einen Test wieder und wieder aus, sollte er jedes Mal gleich antworten: bestanden oder fehlgeschlagen, aber immer gleich. Flaky Tests halten sich nicht daran. Sie bestehen, schlagen fehl, bestehen zweimal und schlagen wieder fehl. Genau diese Unbeständigkeit ist das Problem.
Gute Tests haben ein paar Eigenschaften gemeinsam. Sie prüfen ihr Ergebnis selbst, kein Mensch muss es bestätigen. Sie sind schnell, weil das Feedback zügig zurückkommen soll. Sie sind isoliert und kommen sich deshalb auch im Parallelbetrieb nicht in die Quere. Und sie sind wiederholbar. Flakiness greift genau diese letzte Eigenschaft an, und nur sie.
Den Schaden siehst du in deinem Continuous-Integration-System. CI richtest du aus einem einzigen Grund ein: Du willst wissen, ob eine Änderung sicher ist. Ist der Build grün, sagen alle automatisierten Prüfungen, dass die Änderung so sicher ist, wie sie sein kann. Ein Flaky Test vergiftet dieses Signal. Du weißt nicht mehr, ob Rot einen echten Fehler bedeutet oder nur Rauschen.
Warum Flaky Tests Vertrauen kosten, nicht nur Zeit
Der eigentliche Preis ist das Vertrauen in die CI-Pipeline. Alles, was kein verlässliches Signal liefert, egal ob grün oder rot, nagt daran. Wenn du dem Build nicht mehr glaubst, kannst du ihn dir fast sparen.
Auch der Zeitverlust ist echt. Das Ritual kennen die meisten Teams: Der Build wird rot, jemand zuckt mit den Schultern, sagt „Ach, das macht der manchmal“ und startet ihn neu. Mal läuft er durch, mal nicht. Erst beim zweiten oder dritten Versuch fragt sich jemand, ob da nicht doch ein echter Fehler steckt. Bis dahin ist die Feedbackschleife länger geworden, und ein Teil dessen, was CI leisten sollte, ist verpufft.
Schnelles Feedback auf eine Änderung gehört zu den zentralen Zielen der Softwareentwicklung. Flaky Tests arbeiten direkt dagegen.
Ein Flaky Test kann ein nützlicher Test sein
Flakiness heißt nicht automatisch, dass der Test schlecht ist. Manchmal zeigt er auf etwas, das du wissen solltest. Besteht er, weißt du, dass der Code funktioniert, wenn alles zusammenpasst. Schlägt er fehl, legt er womöglich eine echte Schwäche im System offen.
Am Testframework liegt es selten. Viel öfter steckt ein kurzer Netzwerkfehler dahinter, eine Abhängigkeit, die kurz weg ist, eine Datenbank oder Message Queue, die nicht erreichbar ist, oder eine Race Condition. Mit genau diesen Bedingungen muss dein Produktivcode klarkommen, sobald Nutzerinnen und Nutzer mit ihm arbeiten.
Ein Flaky Test kann also ein Geschenk sein. Er zwingt dich zu fragen, warum er fehlschlägt und ob dein Code dieselbe Situation in Produktion übersteht. Hundert Prozent Verfügbarkeit bekommst du von keiner Abhängigkeit. Wenn ein kurzer Ausfall deinen Test umwirft, trifft derselbe Ausfall auch deine Nutzer.
Der übliche Reflex geht in die falsche Richtung. Wer einen Flaky Test findet, schiebt es gern auf den Test oder die Infrastruktur: „Das hätte einfach funktionieren müssen.“ Ehrlicher ist oft die Antwort, dass der Code mit dem Schluckauf hätte umgehen müssen und es nicht getan hat.
Warum Flaky Tests so schwer zu beheben sind
Die Ursache zu finden, kostet echte Zeit, und deshalb wird die Aufgabe gern vor sich hergeschoben. Ein Fehler, der sich zuverlässig reproduzieren lässt, ist viel leichter zu debuggen als einer, der nur bei jedem zwanzigsten Lauf auftaucht. Wenn sich das Symptom kaum zeigt, zieht sich die Suche.
Zwei Ursachen dominieren. Die erste ist der konkurrierende Zugriff auf gemeinsamen, veränderlichen Zustand. In Java lebt ein statischer Wert zum Beispiel über die gesamte Laufzeit der JVM. Ein Test ändert ihn, ein späterer liest oder ändert ihn wieder, und in dieser Reihenfolge geht alles gut. Läuft die Reihenfolge anders, tauchen Fehler wie aus dem Nichts auf.
Die zweite Ursache ist geänderter gemeinsamer Zustand, etwa in einer Datenbank. Leert die erste Zeile deines Tests eine ganze Tabelle, während ein anderer Test gerade genau diese Tabelle bearbeitet, ist das Chaos perfekt. Für beide Muster gibt es ein verlässliches Indiz: Der Test besteht allein, schlägt aber fehl, sobald er mit anderen zusammen läuft. Dann weißt du, dass du auf der richtigen Spur bist.
Am schwersten zu fassen sind Race Conditions. Simon Stewart hat einmal eine eigene ZIP-Komprimierung in Java geschrieben. Der Test bestand, schlug zweimal fehl und bestand dann wieder, obwohl der Code solide aussah. Schuld war die Auflösung der Zeitstempel: Das ZIP-Format speichert sie auf zwei Sekunden genau, nicht auf eine. Startete der Test in einer geraden Sekunde, bestand er, in einer ungeraden schlug er fehl. Nachdem die Zeitstempel normalisiert waren, lief der Test stabil. Die Suche dorthin war trotzdem zäh.
Mit einem Flaky Test umgehen: Schritt für Schritt
Zuerst nimmst du den Test aus der CI. Deine CI muss ein klares Signal liefern, also hat der Flaky Test in den regulären Läufen vorerst nichts zu suchen. Danach sorgt ein fester Ablauf dafür, dass er nicht in Vergessenheit gerät.
Zum Herausnehmen gibt es zwei Wege. Der einfachste ist eine Ignore- oder Skip-Annotation, die fast jedes Testframework kennt. Verknüpfe sie mit einem Eintrag im Bugtracker, damit der Test nachvollziehbar bleibt. Die Alternative ist eine Ausschlussliste im Repository oder an einer anderen zentralen Stelle, die festhält, welche Tests beim jeweiligen Lauf übersprungen werden.
Das Selenium-Projekt arbeitet mit einer solchen Datei übersprungener Tests, die in die CI einfließt. Eine zentrale Datei schlägt verstreute Annotationen, die schwer zu finden und leicht zu vergessen sind. Wenn alles an einer Stelle steht, siehst du jederzeit, welche Tests ausgesetzt sind.
| Schritt | Was du tust | Warum es wichtig ist |
|---|---|---|
| Herausnehmen | Test per Annotation oder Ausschlussliste aus der CI nehmen | Schützt die Aussagekraft des CI-Signals |
| Nachverfolgen | Mit einem Bug verknüpfen oder in einer zentralen Datei führen | Hält den Test sichtbar und auffindbar |
| Zuständigkeit | Einer namentlich benannten Person zuweisen | Verhindert, dass sich niemand zuständig fühlt |
| Lösen | Debuggen, verkleinern, wiederholen oder löschen | Stellt ein verlässliches Signal her oder entfernt Ballast |
Viele Unternehmen hören nach dem Herausnehmen auf. Der Build ist wieder stabil, das Problem scheint erledigt. Solange aber niemand für den Test zuständig ist, passiert danach nichts mehr.
Jeder Flaky Test braucht eine verantwortliche Person, kein Team
Die Verantwortung für einen Flaky Test gehört einer einzelnen Person, nie einem Team. Gibst du ihn nur an ein Team, landest du bei der Tragik der Allmende: Alle gehen davon aus, dass sich schon jemand anderes kümmert.
Verantwortlich zu sein, heißt nicht, alle Arbeit selbst zu machen. Es heißt, dafür zu sorgen, dass geklärt wird, wie der Test repariert wird. Vielleicht gibt die Person die Aufgabe weiter. Vielleicht behandelt sie den Fall wie jeden anderen eingehenden Bug. Aber eine namentlich benannte Person treibt ihn voran.
Hat der Test eine verantwortliche Person, wird er gelöst. Lass ihn wiederholt laufen und grenze die Ursache ein. Finde heraus, warum er flaky ist, und schreib dann den kleinstmöglichen Test, der zeigt, dass deine Korrektur wirkt. Wer das konsequent macht, landet fast von selbst bei der klassischen Testpyramide: viele kleine Tests an der Basis, wenige große an der Spitze.
Einen Test zu löschen, ist eine legitime Lösung
Du kannst einen Flaky Test auch löschen. Mit einer Versionsverwaltung ist Löschen keine Vernichtung. Stellt sich der Test später doch als wertvoll heraus, holst du ihn zurück.
Oft kommt dann der Einwand, der Test decke „einen wichtigen Ablauf“ ab. Das Argument schneidet in beide Richtungen. Ist der Ablauf wirklich wichtig, lohnt sich die Entwicklungszeit, um den Test stabil zu bekommen. Ist er diese Zeit nicht wert, ist es der Test vermutlich auch nicht. Je wichtiger er angeblich ist, desto höher gehört seine Stabilisierung priorisiert.
Manche Teams geben einem Flaky Test ein Ablaufdatum. Ist er bis dahin nicht repariert, fliegt er als toter Code raus. Er bringt keinen Nutzen und macht nur Arbeit, also darf er weg.
Mehrstufige Teststrategien liefern einen weiteren Grund. Wenn frühere, kleinere Tests das Risiko schon abdecken, das ein großer Test absichern sollte, ist der große Test überflüssig. Flakiness sitzt besonders gern in großen Tests. Einen davon zu entfernen, dessen Abdeckung anderswo schon vorhanden ist, ist ein klarer Gewinn.
Wann Wiederholungen vertretbar sind und wann sie zu teuer werden
Einen Test im selben Build erneut laufen zu lassen, ist ein vertretbarer Kompromiss, aber kein Ideal. Hat ein Test eine Wahrscheinlichkeit von fünf Prozent, zufällig fehlzuschlagen, sinkt sie beim zweiten Lauf auf fünf Prozent von fünf Prozent. Mit jedem Retry wird ein falsches Rot deutlich unwahrscheinlicher.
Bezahlt wird mit Build-Zeit. Ein Flaky Test ist meist ein großer Test, weil er die meisten beweglichen Teile hat. Lässt du einen Test mit vier Minuten Laufzeit fünfmal wiederholen, ist dein Zeitbudget für das Feedback schnell dahin.
Im Schnitt funktionieren etwa drei Retries gut. Entscheidend ist aber dein eigenes Service Level Objective. Sollen CI-Ergebnisse nach zehn Minuten vorliegen und dauert ein durchschnittlicher Test drei Minuten, passen drei Läufe hinein, vier nicht. Du wägst Vertrauen gegen Feedbackgeschwindigkeit ab.
Flaky Tests verhindern, bevor sie in den Build gelangen
Eine Eingangskontrolle hält Flakiness von vornherein draußen. Ein Flaky Test sollte gar nicht erst zum regulären CI-Test werden.
Meta macht das im großen Stil. Bevor ein Test die CI beeinflussen darf, läuft er über Nacht hundertmal parallel zu allen anderen Tests. Nur wenn er dabei komplett stabil bleibt, kommt er in die regulären CI-Läufe. Der ganze Ablauf ist automatisiert, von Hand wäre das nicht zu schaffen.
Für die Idee brauchst du nicht Metas Ressourcen. Lass einen neuen Test vor dem Einchecken zum Beispiel zehnmal lokal laufen und achte auf Ausreißer. Dafür musst du festhalten, welche Tests schon bekannt sind, und schon eine simple Umbenennung löst dann einen neuen Prüflauf aus. Das ist in Ordnung: Ein stabiler Test kommt durch die Kontrolle, ein Flaky Test hätte ohnehin nie hineingehört.
Die zweite Gewohnheit: bekannte Flaky Tests regelmäßig erneut laufen lassen. Manchmal wird eine systemische Ursache an anderer Stelle behoben, und eine ganze Gruppe von Tests läuft plötzlich wieder stabil. Eine zentrale Datei mit ausgesetzten Tests macht das leicht, denn eine Zeile aus einer Liste zu streichen, geht schneller, als im Code nach verstreuten Annotationen zu suchen. Sind die Tests wieder stabil, kehren sie in die regulären CI-Läufe zurück und liefern wieder ein verlässliches Signal.
Wo KI bei Flaky Tests hilft und wo nicht
Beim Debugging ist KI eine echte Hilfe, als alleinige Autorin deiner Tests nicht. Sie kann verschachtelten Code gut analysieren und zum Beispiel darauf hinweisen, dass ein Wert nie gesetzt wird oder dass das Problem in einer bestimmten Zeile steckt. Wenn du der Ursache eines sporadischen Fehlschlags nachjagst, ist das wirklich nützlich.
Das Risiko ist falsche Sicherheit.
“Das ist wie ein Betrunkener in der Bar, der eine Meinung hat und sie mit großem Nachdruck vertritt. Und er kann trotzdem völlig danebenliegen.”
(Simon Stewart)
Behandle deshalb jede KI-Antwort als Vorschlag, den ein Mensch prüft. Du kannst auch ein zweites Modell bitten, die Diagnose des ersten zu überprüfen. Sind sich beide einig, dass eine bestimmte Zeile den Fehlschlag erklärt, hast du einen vernünftigen Startpunkt.
KI kann viele plausible Testfälle erzeugen. Simon Stewart legt die Bedingungen aber lieber selbst fest, statt diese Entscheidung abzugeben. KI ist ein starkes Werkzeug für deine Arbeit, kein Ersatz dafür. Das eigentliche Ziel bleibt dasselbe: eine sichere Änderung so schnell wie möglich zu den Nutzerinnen und Nutzern bringen. Je weniger Code du dabei mitschleppst, desto besser.
Häufig gestellte Fragen
Welche Eigenschaften sollte ein zuverlässiger automatisierter Test haben?
Gute Tests sind selbstüberprüfend, schnell, isoliert und wiederholbar. Selbstüberprüfend bedeutet, dass niemand das Ergebnis bestätigen muss. Isoliert bedeutet, dass sie gleichzeitig laufen können, ohne sich gegenseitig zu behindern. Unbeständigkeit beeinträchtigt allein die Wiederholbarkeit: Derselbe Code führt bei einem Durchlauf zum Erfolg, beim nächsten zur Fehlerwirkung. Die anderen drei Qualitäten können intakt bleiben, während der Test als Signal bereits unbrauchbar ist.
Ist ein sporadischer Testfehler immer die Schuld des Tests?
Nein. Die Ursache liegt selten im Test-Framework selbst. Vorübergehende Netzwerkfehler, eine kurzzeitig ausgefallene Abhängigkeit, eine nicht verfügbare Datenbank oder Nachrichtenwarteschlange sowie Race-Conditions sind die üblichen Übeltäter. Genau das sind die Bedingungen, denen Produktionscode ausgesetzt ist, sobald er in den Händen der Nutzer ist. Wenn eine vorübergehende Fehlerwirkung den Test zum Scheitern bringt, wird dieselbe Fehlerwirkung auch deine Nutzer treffen.
Wie kann ich herausfinden, warum ein Test nur gelegentlich fehlschlägt?
Führe den Test isoliert aus. Ein Test, der isoliert besteht, aber fehlschlägt, wenn er zusammen mit anderen ausgeführt wird, deutet direkt auf einen gemeinsam genutzten Zustand hin. Zwei Muster dominieren: Nebenläufigkeit beim Zugriff auf gemeinsam genutzten, veränderbaren Zustand, wie zum Beispiel einen statischen Wert, der in der gesamten JVM vorhanden ist, und Tests, die Datenbankzeilen löschen oder ändern, während ein anderer Test gerade dabei ist, dieselbe Tabelle zu modifizieren.
Reicht es aus, einen unzuverlässigen Test aus der CI zu entfernen, um das Problem zu lösen?
Nein. Die meisten Unternehmen belassen es dabei, weil der Build wieder stabil ist und der Fehler behoben zu sein scheint. Ohne einen Verantwortlichen passiert danach nichts. Weise den Test einer bestimmten Person zu, statt einem Team, sonst setzt die „Tragödie der Allmende“ ein und jeder geht davon aus, dass sich jemand anderes darum kümmern wird. Verantwortung bedeutet, dafür zu sorgen, dass die Fehlerbehebung erfolgt, nicht unbedingt, die ganze Arbeit selbst zu erledigen.
Ist das erneute Ausführen eines fehlgeschlagenen Tests im selben Build eine akzeptable Übergangslösung?
Es ist ein Kompromiss, kein Ideal. Ein Test mit einer Wahrscheinlichkeit von fünf Prozent für eine unzuverlässige Fehlerwirkung sinkt beim zweiten Durchlauf auf fünf Prozent von fünf Prozent, sodass die Wahrscheinlichkeit eines falschen „roten“ Ergebnisses schnell abnimmt. Der Preis dafür ist die Build-Zeit. Im Durchschnitt funktionieren etwa drei Wiederholungsversuche gut, aber vergleiche das mit deinem eigenen Ziel: Bei einem Zehn-Minuten-Ziel und einem Drei-Minuten-Test passen drei Durchläufe, vier hingegen nicht.
Was soll ich mit einem unzuverlässigen Test tun, der einen geschäftskritischen Workflow abdeckt?
Dieses Argument ist ein zweischneidiges Schwert. Wenn der Arbeitsablauf wirklich wichtig ist, rechtfertigt das den technischen Aufwand, den Test absolut zuverlässig zu machen, und je wichtiger du ihn einschätzt, desto höher ist die Priorität, ihn zu stabilisieren. Wenn dieser Aufwand nicht gerechtfertigt ist, lohnt es sich wahrscheinlich nicht, den Test beizubehalten. Dank der Versionskontrolle ist das Löschen reversibel, sodass ein gelöschter Test wiederhergestellt werden kann.
Können unzuverlässige Tests von vornherein aus dem Build ferngehalten werden?
Ja, indem neue Tests einer Vorabprüfung unterzogen werden, bevor sie die CI beeinflussen. Meta führt jeden neuen Test über Nacht hundertmal parallel mit allen anderen Tests durch und lässt ihn nur zu, wenn er vollkommen stabil bleibt. Der gesamte Prozess ist dabei automatisiert. Bei einem kleineren Budget führe einen neuen Test zehnmal lokal aus und achte auf Instabilitäten, bevor du ihn hinzufügst.
Können KI-Tools bei unzuverlässigen Tests helfen?
Sie helfen beim Debugging, nicht beim Schreiben der Tests. KI analysiert komplexen Code gut und kann darauf hinweisen, dass ein Wert nie gesetzt wird oder dass das Problem in einer bestimmten Zeile liegt. Das ist nützlich, wenn man einer inkonsistenten Fehlerwirkung auf der Spur ist. Das Risiko ist falsches Vertrauen, daher überprüft ein Mensch jede Ausgabe. Ein zweites Modell zu bitten, die erste Diagnose zu verifizieren, bietet einen vernünftigen Ausgangspunkt.


