moworks.dev
Zurück zum Blog

9. September 2026

5 Arten von Softwaretests für zuverlässigere Systeme

Ein praktischer Leitfaden zu Unit-, Integrations-, Canary-, Fault-Injection- und Performancetests mit Erfahrungen aus fast einem Jahrzehnt bei Amazon.

Unterschiedliche Tests decken unterschiedliche Probleme auf. Manche prüfen, ob deine Logik funktioniert. Andere überprüfen, ob deine Dienste zusammenspielen, ob das Produktivsystem noch einwandfrei läuft oder was passiert, wenn eine Abhängigkeit ausfällt.

Ich habe bei Amazon fast ein Jahrzehnt lang solche Tests entworfen und implementiert. Hier ist meine Einschätzung dazu, wo die einzelnen Testarten ihren Mehrwert bieten.

Unit-Tests

Unit-Tests prüfen kleine Codeabschnitte isoliert. Sie liegen üblicherweise direkt beim Anwendungscode und eignen sich besonders für bedingte Logik, reguläre Ausdrücke, Datentransformationen und Konvertierungen. Sie laufen während der Entwicklung und bei der Prüfung von Pull Requests, vor dem Deployment.

Was ich absolut nicht ausstehen kann, ist die pauschale Vorgabe von 100 % Testabdeckung durch Unit-Tests.

Ich habe in Codebasen gearbeitet, in denen das zu Bergen von Tests für triviale Getter und Setter geführt hat. Harmlose Refactorings erforderten Anpassungen an Tests, die hauptsächlich prüften, wie der Code geschrieben war, statt ob er sich korrekt verhielt.

Die Testabdeckung sagt dir, welcher Code während eines Tests ausgeführt wurde. Sie sagt dir nicht, ob der Test sinnvoll war.

Teste relevantes Verhalten und Grenzfälle. Bitte schreib keine Tests, nur um eine Prozentzahl zu erreichen.

Integrations- und End-to-End-Tests

Integrationstests prüfen, ob die Teile eines Systems zusammenarbeiten. Ein API-Test kann beispielsweise eine laufende Anwendung aufrufen und ihre Antwort überprüfen. Dabei werden auch die Verarbeitung der Anfrage, die Geschäftslogik und die Datenbankverbindung getestet.

Dafür ist keine deployte Umgebung erforderlich. Tests gegen eine solche Umgebung können aber zusätzlich Probleme mit Konfiguration, Authentifizierung und Verbindungen aufdecken, die isolierte Tests übersehen. Welche Abhängigkeiten echt und welche simuliert sind, bestimmt, was du tatsächlich überprüfst.

Hier sehe ich oft einen deutlich höheren Nutzen im Verhältnis zur Menge des geschriebenen Testcodes. Eine einzige Anfrage kann einen relevanten Ablauf durch die Anwendung testen.

UI-Tests mit Werkzeugen wie Selenium oder Cypress können das auf die Nutzererfahrung ausweiten: eine Seite öffnen, einen Button anklicken und das Ergebnis prüfen. Wenn sie dabei das Frontend und echte Backend-Dienste gemeinsam testen, handelt es sich um End-to-End-Tests.

Sie sind langsamer und wartungsaufwendiger als Unit-Tests. Konzentriere dich deshalb auf wichtige Nutzerabläufe statt auf jede mögliche Interaktion.

Canary-Tests

Mit Canary-Tests meine ich regelmäßig wiederkehrende synthetische Prüfungen eines laufenden Systems. Anders als Deployment-Prüfungen laufen sie nach einem festen Zeitplan, unabhängig davon, ob neuer Code ausgeliefert wurde.

Diese Unterscheidung ist wichtig, denn Deployments sind nicht die einzige Ursache für Ausfälle.

Ein Speicherleck verursacht möglicherweise erst nach Stunden Probleme. Jemand könnte die Konfiguration außerhalb deiner Release-Pipeline ändern. Ein Dienst, von dem deine Anwendung abhängt, könnte seine API ändern. Seine eigenen Tests bestehen weiterhin, aber deine Anwendung funktioniert nicht mehr.

Ein Canary-Test führt regelmäßig einen relevanten Vorgang aus, damit du solche Fehler automatisch erkennen kannst. Er diagnostiziert zwar kein Speicherleck, kann aber zeigen, dass Anfragen langsamer werden oder fehlschlagen.

Verknüpfe diese Prüfungen mit Alarmen und sinnvollen Fehlerschwellen, damit jemand die Ursache untersuchen und den Betrieb wiederherstellen kann.

Fault-Injection- oder „Gremlin“-Tests

Fault-Injection-Tests führen absichtlich Fehler herbei, um zu prüfen, wie das System reagiert und sich davon erholt. Du könntest eine Abhängigkeit unerreichbar machen, Verzögerungen einbauen oder Anfragen gezielt fehlschlagen lassen.

Stell dir eine Produktseite vor, die von einem Preisdienst abhängt. Fällt dieser Dienst aus, soll die Seite vielleicht weiter funktionieren und „Preise derzeit nicht verfügbar“ anzeigen.

Aber unterstützt die Implementierung das überhaupt?

Können API und UI mit einem fehlenden Preis umgehen? Werden Anfragen nach einer bestimmten Zeit abgebrochen? Ist die Zahl der Wiederholungsversuche begrenzt, oder überlasten sie den Preisdienst, während er sich gerade erholt?

Fault Injection macht aus diesen Fragen wiederholbare Prüfungen.

Kleine, kontrollierte Szenarien können in CI/CD laufen. Umfangreichere Chaos-Experimente können separat stattfinden, mit Schutzmaßnahmen, die ihre Auswirkungen begrenzen. In jedem Fall solltest du getestet haben, wie sich dein System nach einem Fehler erholt.

Performance- und Durchsatztests

Performancetests prüfen, wie sich ein System unter Last verhält. Wie viele Anfragen kann es verarbeiten, während Antwortzeiten und Fehler innerhalb akzeptabler Grenzen bleiben?

Die Testlast muss der tatsächlichen Nutzung ähneln. Dazu gehört eine repräsentative Mischung aus Lese- und Schreibzugriffen, kleinen und großen Anfragen sowie häufigen und rechenintensiven Vorgängen. Immer wieder denselben wenig aufwendigen Endpunkt zu testen, sagt dir kaum etwas über die gesamte Anwendung.

Erhöhe die Last und miss abgeschlossene Anfragen, Fehler und Antwortzeiten. Berücksichtige dabei auch die besonders langsamen Antworten. Ein Durchsatzziel ist bedeutungslos, wenn du es nur erreichst, indem du Fehler zurückgibst oder Nutzer warten lässt.

Für reproduzierbare Vergleiche zwischen Releases hilft eine feste Umgebung, manchmal auch eine einzelne Instanz. So lassen sich Veränderungen der Anwendungsperformance besser isolieren. Separate Tests sollten prüfen, wie das Gesamtsystem skaliert.

Diese Tests können in einer eigenen CI/CD-Phase laufen, mit klaren Grenzwerten für Durchsatz, Fehlerrate und Antwortzeiten.

Das Zusammenspiel

Jede Testebene beantwortet eine andere Frage: Funktioniert die Logik? Arbeiten die Komponenten zusammen? Läuft das System noch einwandfrei? Verkraftet es Ausfälle? Und hält es der Last stand?

Robuste Systeme brauchen Antworten auf alle fünf Fragen. Die Gewichtung hängt vom Produkt ab. Allein mit dem Streben nach hoher Unit-Test-Abdeckung kommst du aber nicht ans Ziel.

Ich habe bei Amazon fast ein Jahrzehnt lang solche Tests entworfen und implementiert. Wenn du jemanden suchst, der dir hilft, zuverlässige Software und einen verlässlichen Release-Prozess aufzubauen, unterstütze ich dich gerne. Melde dich bei mir.

Hinweis: Dieser Artikel wurde mithilfe von KI aus dem englischen Original ins Deutsche übersetzt.