Wo sollte jeder Test in Ihrer Pipeline ausgeführt werden?
Sie pushen Code und warten. Fünf Minuten vergehen. Zehn Minuten. Die Pipeline läuft immer noch. Schließlich schlägt sie fehl – aber der Fehler stammt von einem Unit-Test, der in den ersten dreißig Sekunden hätte laufen sollen. Sie haben gerade zehn Minuten Ihrer Zeit und Rechenressourcen verschwendet.
Das ist der Kernkonflikt im Pipeline-Design: Zu viele Tests früh, und Entwickler warten zu lange auf Feedback. Zu wenige, und kritische Fehler schlüpfen bis ins Staging oder die Produktion. Die Lösung ist nicht, alles überall auszuführen, sondern jeden Testtyp in der Phase zu platzieren, in der er den größten Nutzen bei geringsten Kosten bietet.
Das Prinzip: Schnell und günstig zuerst
Die Regel ist einfach: Führen Sie schnelle, günstige Tests früh aus. Führen Sie langsame, teure Tests später aus, und nur wenn die früheren Tests bestanden sind.
„Günstig“ bedeutet hier geringe Rechenzeit, geringer Umgebungsaufbau und geringe Datenvorbereitung. Ein Unit-Test, der in Millisekunden läuft und keine Datenbank benötigt, ist günstig. Ein End-to-End-Test, der eine vollständige Umgebung hochfährt, Daten vorsät und eine Benutzerreise simuliert, ist teuer.
„Schnell“ bedeutet die Feedback-Schleife. Ein Entwickler sollte innerhalb von ein oder zwei Minuten wissen, ob seine Änderung etwas Grundlegendes kaputt gemacht hat. Dreißig Minuten zu warten, um einen einfachen Logikfehler zu entdecken, ist nicht akzeptabel.
Hier geht es nicht um Richtlinien. Es geht um Durchsatz. Je schneller Entwickler Feedback bekommen, desto schneller beheben sie Probleme. Je teurer ein Test auszuführen ist, desto seltener sollte er laufen.
Phase für Phase: Wo Tests hingehören
Commit-Phase: Unit-Tests und Vertragstests
Das folgende Flussdiagramm fasst die empfohlene Testplatzierung über die Pipeline-Phasen zusammen:
Wenn ein Entwickler Code pusht, laufen als Erstes die Unit-Tests. Diese Tests sind schnell, benötigen keine externen Abhängigkeiten und überprüfen, ob einzelne Verhaltensweisen korrekt funktionieren. Ein guter Unit-Test beweist, dass ein sinnvolles Verhalten – aus Sicht des Aufrufers oder Benutzers – das erwartete Ergebnis liefert. Er kümmert sich nicht um interne Implementierungsdetails.
Manche Teams führen in dieser Phase auch Vertragstests aus. Wenn die Änderung eine Schnittstelle oder einen API-Vertrag betrifft, verhindert die frühzeitige Überprüfung Überraschungen nachgelagerter Systeme. Vertragstests sind normalerweise schnell genug, um zusammen mit Unit-Tests zu laufen.
Was hier nicht laufen sollte: Integrationstests, End-to-End-Tests oder Tests, die eine Datenbank, einen externen Dienst oder eine vollständige Umgebung benötigen. Diese gehören später hin.
Build-Phase: Integrationstests und erneut Vertragstests
Nachdem der Code kompiliert wurde und ein Artefakt oder Container-Image erzeugt hat, kommen Integrationstests ins Spiel. In dieser Phase existiert die Anwendung als ausführbare Einheit. Integrationstests prüfen, ob die Anwendung korrekt mit ihren Abhängigkeiten verbunden ist – Datenbanken, Message Queues oder anderen Diensten.
Diese Tests verwenden typischerweise Test-Doubles oder leichtgewichtige Container, keine vollständigen Staging-Umgebungen. Sie überprüfen, ob die Verkabelung korrekt ist: Die Anwendung kann eine Verbindung öffnen, eine Abfrage senden und eine Antwort verarbeiten.
Vertragstests laufen hier oft erneut, gegen das erstellte Artefakt. Dies stellt sicher, dass der kompilierte Code den Vertrag immer noch erfüllt, nicht nur der Quellcode.
Staging-Phase: End-to-End-Tests und Smoke-Tests
Staging sollte die Produktion so genau wie möglich abbilden. Hier führen Sie End-to-End-Tests aus – aber nur für kritische Benutzerreisen. Nicht jede Seite, nicht jedes Feature. Nur die Abläufe, die am wichtigsten sind: Login, Checkout, Suche oder was auch immer Ihre Kern-Geschäftslogik ist.
End-to-End-Tests sind langsam und teuer. Sie für jede kleine Änderung auszuführen, verschwendet Zeit. Heben Sie sie für Änderungen auf, die kritische Pfade berühren oder neue Funktionalitäten einführen.
Smoke-Tests laufen ebenfalls hier. Ein Smoke-Test ist eine schnelle Überprüfung, ob die Anwendung nach der Bereitstellung korrekt antwortet. Wenn der Smoke-Test im Staging fehlschlägt, fahren Sie nicht mit der Produktion fort. Stoppen Sie die Pipeline und untersuchen Sie den Fehler.
Produktionsphase: Smoke-Tests und synthetische Transaktionen
In der Produktion müssen Tests minimal sein und dürfen Benutzer nicht beeinträchtigen. Führen Sie unmittelbar nach der Bereitstellung einen Smoke-Test durch, um zu bestätigen, dass die wichtigsten Endpunkte mit den richtigen Statuscodes antworten. Dies ist keine tiefgehende Validierung – nur ein Plausibilitätscheck, dass die Bereitstellung die Anwendung nicht beschädigt hat.
Synthetische Transaktionen laufen periodisch, nicht direkt nach jeder Bereitstellung. Sie simulieren Benutzerinteraktionen – wie das Einloggen oder das Abschließen eines Kaufs – um Regressionen zu erkennen, die das Staging nicht abgefangen hat. Da sie nach einem Zeitplan laufen, fangen sie Probleme auf, die im Laufe der Zeit auftreten, wie Speicherlecks oder Datenkorruption.
Was Sie nicht tun sollten
Führen Sie nicht alle Tests in allen Phasen aus. Das ist der häufigste Fehler. End-to-End-Tests in der Commit-Phase auszuführen, ergibt keinen Sinn – die Umgebung ist nicht bereit, und das Feedback ist zu langsam. Unit-Tests in der Produktionsphase erneut auszuführen, ist verschwenderisch – die Logik hat sich zwischen Staging und Produktion nicht geändert.
Eine gute Faustregel: Wenn ein Test in einer früheren Phase bestanden wurde, wiederholen Sie ihn nicht in einer späteren Phase, es sei denn, die Umgebungsänderung könnte das Ergebnis beeinflussen. Unit-Tests hängen nicht von der Umgebung ab, daher müssen sie nicht erneut ausgeführt werden. Smoke-Tests hängen von der Bereitstellung ab, daher müssen sie in jeder neuen Umgebung ausgeführt werden.
Risikobasiertes Testen in der Pipeline
Die Platzierung von Tests hängt auch vom Risiko ab. Eine Änderung am Zahlungssystem oder der Authentifizierungslogik erfordert mehr Testebenen. Eine Änderung einer Button-Farbe oder ein Tippfehler in einem Label benötigt nur Unit-Tests und einen Smoke-Test.
Dies ist risikobasiertes Testen, angewendet auf die Pipeline: Je höher das Risiko der Änderung, desto mehr Testebenen muss sie durchlaufen. Je geringer das Risiko, desto schneller kann sie durch die Pipeline laufen.
Einige Teams implementieren dies, indem sie Änderungen taggen oder Branch-Richtlinien verwenden. Kritische Pfade erfordern End-to-End-Tests im Staging. Nicht-kritische Pfade überspringen sie. Dies hält die Pipeline für risikoarme Änderungen schnell, während die Sicherheit für risikoreiche Änderungen erhalten bleibt.
Praktische Checkliste für die Testplatzierung
- Unit-Tests laufen in der Commit-Phase. Schnell, keine Abhängigkeiten, sofortiges Feedback.
- Vertragstests laufen in der Commit- und Build-Phase. Überprüfen Sie Schnittstellen früh und gegen das Artefakt.
- Integrationstests laufen in der Build-Phase. Prüfen Sie Verbindungen zu Abhängigkeiten mit Containern oder Test-Doubles.
- End-to-End-Tests laufen nur in der Staging-Phase. Decken Sie kritische Benutzerreisen ab, nicht jedes Feature.
- Smoke-Tests laufen in der Staging- und Produktionsphase. Schnelle Plausibilitätschecks nach jeder Bereitstellung.
- Synthetische Transaktionen laufen periodisch in der Produktion. Fangen Sie Regressionen im Laufe der Zeit ab.
- Wiederholen Sie keine Tests über Phasen hinweg, es sei denn, die Umgebungsänderung ist relevant.
- Passen Sie die Testebenen basierend auf dem Risiko an. Risikoreiche Änderungen erhalten mehr Abdeckung.
Das Fazit
Ein gut platzierter Test gibt Ihnen schnelles Feedback, wenn Sie es brauchen, und gründliche Validierung, wenn Sie es sich leisten können. Das Ziel ist nicht, alles überall zu testen. Das Ziel ist, den richtigen Fehler zur richtigen Zeit zu fangen, damit Ihr Team ihn beheben kann, bevor er die Produktion erreicht.