Automatisierte Tests sind einer der wichtigsten Bestandteile von Continuous Integration und Deployment. Wenn Sie kontinuierliche Tests in Ihre DevOps-Pipelines integrieren, können Sie die Qualität Ihrer Software erheblich verbessern.
Bei Continuous Integration und Continuous Deployment (CI/CD) geht es darum, häufige, inkrementelle Änderungen zu liefern, damit Sie regelmäßig Feedback zu Ihrem Produkt oder Service erhalten. Die Qualität des Produkts sollte jedoch nicht darunter leiden, wenn schneller und häufiger bereitgestellt wird. Schließlich erwarten Ihre Benutzer*innen eine stabile und funktionierende Software – selbst wenn sie lautstark nach dem nächsten coolen Feature verlangen.
Ein zuverlässiger und gründlicher automatisierter Testprozess, der Vertrauen in neue Builds entstehen lässt, ist daher in der Praxis von entscheidender Bedeutung für IhreCI/CD-Praktiken.
Tests sind unerlässlich für die Sicherung der Softwarequalität, und daher sind sie seit langem ein Bestandteil der Softwareentwicklungspraktiken.
Mit der Wasserfall-Methode findet das manuelle Testen oder die QA-Phase statt, nachdem der Code entwickelt und integriert wurde.
💡NB: Die Wasserfallstrategie, oft auch als Wasserfallmodell bezeichnet, ist ein linearer und sequentieller Ansatz zur Softwareentwicklung und zum Projektmanagement. Es ist eines der frühesten Modelle für die Software-Entwicklung und zeichnet sich durch einen strukturierten und systematischen Ablauf durch verschiedene Phasen aus.
Ein Nachteil dabei ist, dass Sie erst lange nach dem Schreiben des Codes überprüfen können, ob er wie vorgesehen funktioniert. Zu diesem Zeitpunkt kann bereits viel mehr Code darauf aufgebaut worden sein, was die Behebung von Problemen erheblich erschwert. Dies wiederum verlangsamt die Bereitstellung neuer Funktionen und Fehlerbehebungen.
Im Gegensatz dazu werden bei einem agilen Ansatz kurze, iterative Entwicklungszyklen bevorzugt, sodass Sie häufiger neue Versionen herausgeben, Feedback von Ihren Benutzer*innen erhalten und fundierte Entscheidungen darüber treffen können, was als Nächstes entwickelt werden soll. CI/CD unterstützt diese Arbeitsweise, indem es die Schritte zwischen dem Schreiben Ihres Codes und der Freigabe an die Benutzer*innen automatisiert und so den gesamten Prozess zuverlässiger und effizienter macht.
Um mit dieser DevOps-Methode erfolgreich zu sein, müssen Sie Ihre Codeänderungen regelmäßig festschreiben, erstellen und testen – idealerweise mehrmals am Tag. Doch selbst in einem kleinen Team ist es nicht realistisch, einmal am Tag oder öfter einen kompletten Satz manueller Tests durchzuführen. Aus diesem Grund sind automatisierte Tests ein wesentlicher Bestandteil jeder CI/CD-Pipeline.
Obwohl das Schreiben von automatisierten Tests im Vorfeld eine gewisse Zeit in Anspruch nimmt, macht sich die Arbeit bald bezahlt, insbesondere wenn Sie beginnen, Änderungen häufiger zu übertragen und bereitzustellen. Die Investition in automatisierte Tests bietet mehrere wichtige Vorteile, darunter:
Automatisierte Tests entlasten Ihr QS-Team zwar von vielen langweiligen, sich wiederholenden Aufgaben, aber sie machen sie nicht überflüssig. Neben dem Definieren und Priorisieren von entsprechenden Fällen sollte das QA-Team auch an der Erstellung automatisierter Tests beiteiligt sein, häufig in Zusammenarbeit mit dem Entwicklerteam. Wie wir später sehen werden, gibt es außerdem Tests, die sich nicht automatisieren lassen – auch für diese braucht man ein QA-Team.
Automatisierte Tests finden in mehreren Phasen der Pipeline statt.
Bei CI/CD und QS-Automatisierung geht es um kurze Feedback-Schleifen, die Ihrem Team helfen, Probleme so früh wie möglich zu erkennen.
Es ist viel einfacher, Probleme kurz nach ihrem Entstehen zu beheben, denn dadurch vermeiden Sie, dass noch mehr Code geschrieben wird, der auf einer fehlerhaften Grundlage aufbaut. Auch für die Teammitglieder selbst ist es effizienter, Korrekturen zeitnah vorzunehmen, bevor sie zum nächsten Schritt übergehen und den Kontext verlieren.
Viele Build-Test-Automatisierungstools unterstützen die Integration mit CI/CD, sodass Sie die Testdaten in die Pipeline einspeisen und die Tests schrittweise ausführen können. Die Ergebnisse werden nach jedem Schritt bereitgestellt. Mit einem entsprechenden CI-Tool können Sie basierend auf den Testergebnissen einer Phase entscheiden, ob der Build in die nächste Phase vorrücken soll.
Um die Pipeline durch automatisierte Tests optimal zu nutzen, ist es im Allgemeinen sinnvoll, die Build-Tests so anzuordnen, dass die schnellsten Tests zuerst ausgeführt werden. Auf diese Weise erhalten Sie früher Feedback und nutzen Ihre Testumgebungen effizienter, da sichergestellt wird, dass die ersten Tests bestanden wurden, bevor längere, aufwendigere Tests gestartet werden.
Um bei der Erstellung und Ausführung automatisierter Tests eine sinnvolle Prioritätenfolge zu finden, ist es hilfreich, das Konzept der Testpyramide zur Hilfe zu ziehen.
Die Testpyramide ist ein Tool zur Priorisierung automatisierter Tests in einer CI/CD-Pipeline, sowohl hinsichtlich der relativen Anzahl der Tests als auch der Reihenfolge der Ausführung.
In der ursprünglich von Mike Cohn definierten Testpyramide befinden sich unten die Unit-Tests, darüber die Servicetests und oben die UI-Tests.
Die Testpyramide besteht aus den folgenden Schritten:
Welche Arten von CI/CD-Tests sollten Sie in Betracht ziehen? Sehen wir uns die Optionen an.
Unit-Tests bilden zu Recht die Basis der Testpyramide. Sie sollen sicherstellen, dass der Code wie erwartet funktioniert, indem die kleinstmögliche Verhaltenseinheit getestet wird. Wenn Sie z. B. eine Wetter-App entwickeln, könnte die Umrechnung von Werten von Grad Celsius in Fahrenheit Teil einer größeren Funktion sein. Sie können Unit-Tests verwenden, um zu prüfen, ob die Temperaturumwandlungsfunktion die erwarteten Ergebnisse für bestimmte Werte liefert. Bei jeder Änderung eines damit zusammenhängenden Teils des Codes können Sie mit diesen Unit-Tests überprüfen, ob dieser bestimmte Aspekt wie beabsichtigt funktioniert, ohne die App jedes Mal neu erstellen und ausführen zu müssen.
In Teams, die sich entschieden haben, in das Schreiben von Unit-Tests zu investieren, werden diese in der Regel vom Entwicklerteam direkt beim Hinzufügen des entsprechenden Codes erstellt. Die testgetriebene Entwicklung (TDD) verankert diesen Prozess (wie wir weiter unten erläutern werden), aber TDD ist keine Voraussetzung für die Verwendung von Unit-Tests.
Ein anderer Ansatz ist die Festlegung, dass Unit-Tests Teil der Definition von „done“ für jede Entwicklungsaufgabe sind, und die Überprüfung, ob diese Tests vorhanden sind, bei der Durchführung von Code-Reviews oder anhand von Code-Coverage-Berichten.
Wenn Sie an einem vorhandenen System arbeiten, für das noch keine Unit-Tests vorliegen, ist das Abdecken des gesamten Codebestands „aus dem Stand“ oft ein illusorisches Ziel. Obwohl eine weitflächige Coverage mit Unit-Tests empfehlenswert ist, können Sie mit so viel beginnen, wie gerade möglich ist, und im Laufe der Zeit darauf aufbauen.
Wenn Ihr Code derzeit keine gute Coverage durch Unit-Tests aufweist, sollten Sie sich mit Ihrem Team zusammensetzen, um Unit-Tests zu jedem Teil des Codes hinzuzufügen, den Sie bearbeiten. Mit dieser Strategie wird sichergestellt, dass der gesamte neue Code abgedeckt wird, und der vorhandene Code wird entsprechend den am häufigsten verwendeten Funktionen priorisiert.
Mit Integrationstests stellen Sie sicher, dass die Interaktionen zwischen mehreren Teilen Ihrer Software, z. B. zwischen Anwendungscode und einer Datenbank oder Aufrufen einer API, wie erwartet funktionieren.
Es kann hilfreich sein, Integrationstests in breit und eng gefasste Tests zu unterteilen. Bei engen Integrationstests wird die Interaktion mit einem anderen Modul mithilfe eines Testgerüsts anstelle des eigentlichen Moduls getestet. Bei breiten Integrationstests wird die tatsächliche Komponente oder der tatsächliche Dienst verwendet. Um auf das Beispiel einer Wetter-App zurückzukommen, könnte ein breiter Integrationstest Vorhersagedaten von einer API abrufen, während ein enger Test Mock-Daten verwenden würde.
Abhängig von der Komplexität Ihres Projekts und der Anzahl der internen und externen Services kann es sinnvoll sein, eine Schicht enger Integrationstests zu schreiben, die schneller ausgeführt werden als breite Integrationstests (da sie nicht die Verfügbarkeit anderer Systemteile voraussetzen).
Wenn die engen Integrationstests erfolgreich abgeschlossen sind, können Sie anschließend eine Reihe von breiten Integrationstests durchführen. Da diese Tests länger dauern und einen höheren Wartungsaufwand erfordern, sollten Sie sie auf Bereiche Ihres Produkts oder Ihrer Dienstleistung mit höherer Priorität beschränken.
End-to-End-Tests, auch als Full-Stack-Tests bezeichnet, betreffen die gesamte Anwendung. Sie werden in der Regel verwendet, um geschäftliche Anwendungsfälle zu validieren, z. B. ob ein*e Benutzer*in ein Konto erstellen oder eine Transaktion abschließen kann.
Automatisierte End-to-End-Tests können zwar über eine grafische Benutzeroberfläche ausgeführt werden, müssen es aber nicht; ein API-Aufruf kann auch mehrere Teile des Systems testen (obwohl APIs auch mit Integrationstests geprüft werden können).
Die Testpyramide empfiehlt eine geringere Anzahl dieser Tests, nicht nur aufgrund der längeren Dauer, sondern auch, weil sie oft instabil sind. Jede Änderung an der Benutzeroberfläche kann diese Tests aus der Bahn werfen, was einerseits störendes Rauschen in den Build-Testergebnissen verursacht und andererseits Zeitaufwand für die Aktualisierung des Tests erfordert. Es lohnt sich, End-to-End-Tests sorgfältig zu gestalten und dabei den Abdeckungsumfang der auf einer niedrigeren Ebene angesiedelten Tests zu berücksichtigen, um den Mehrwert zu maximieren.
Die verhaltensgesteuerte Entwicklung (Behavior Driven Development, BDD) ist ein kollaborativer Ansatz für die Softwareentwicklung, der die Kommunikationslücke zwischen Entwickler*innen, Tester*innen und Geschäftsinteressent*innen schließt. Es erweitert TDD, indem es den Schwerpunkt auf das Verhalten der Software und nicht auf ihre Implementierung legt.
BDD kann eine nützliche Strategie für die Entwicklung sowohl von Integrations- als auch von End-to-End-Tests darstellen. Einige der wichtigsten Aspekte sind:
Obwohl die Testpyramide keinen Hinweis auf Leistungstests enthält, sind sie dennoch eine Überlegung wert, insbesondere wenn Stabilität und Geschwindigkeit wichtige Produktkriterien sind.
Der Überbegriff „Performancetest“ umfasst eine Reihe von Teststrategien, mit denen überprüft werden kann, wie sich Ihre Software in einer Live-Umgebung verhält:
Diese Testarten sollen nicht nur bestätigen, dass die Software mit den definierten Parametern zurechtkommt, sondern auch überprüfen, wie sie sich bei Überschreitung dieser Parameter verhält. Idealerweise sollte es dabei zu einem geordneten Herunterfahren statt einem unkontrollierten Crash kommen.
Sowohl Performance- als auch End-to-End-Tests erfordern Umgebungen, die der Produktionsumgebung sehr ähnlich sind. Möglicherweise werden auch Build-Testdaten benötigt. Damit ein automatisiertes Testsystem Vertrauen in die getestete Software schafft, ist es wichtig, die Tests jedes Mal auf die gleiche Weise auszuführen. Dazu gehört auch, dass die Testumgebungen zwischen den verschiedenen Läufen gleich bleiben (wobei sie bei Änderungen in der Produktionsumgebung entsprechend angepasst werden sollten).
Die manuelle Verwaltung dieser Umgebungen kann sehr zeitaufwändig sein. Die Automatisierung des Prozesses der Erstellung und des Abbaus von Vorproduktionsumgebungen bei jedem neuen Build spart Ihnen Zeit und gewährleistet ein konsistentes und zuverlässiges automatisiertes Testverfahren.
Die testgetriebene Entwicklung (Test-driven development, TDD) ist ein Entwicklungsansatz, der seinen Ursprung in der extremen Programmierung (XP) hat. Bei TDD besteht der erste Schritt darin, eine Liste von Testfällen für die hinzuzufügende Funktionalität zu schreiben. Sie nehmen sich dann einen Testfall nach dem anderen vor, schreiben den (fehlgeschlagenen) Test dafür und schreiben dann den Code, damit der Test erfolgreich ist. Schließlich überarbeiten Sie den Code nach Bedarf, bevor Sie zum nächsten Testfall übergehen. Dieser Prozess lässt sich als „Red, green, refactor“ oder „Make it work; make it right“ zusammenfassen.
Einer der Hauptvorteile von TDD ist, dass es Sie zwingt, automatisierte Tests für jeden neuen Code, den Sie schreiben, hinzuzufügen. Das bedeutet, dass die Test-Coverage ständig zunimmt, was ein schnelles und regelmäßiges Feedback bei jeder Änderung des Codes ermöglicht. Weitere Vorteile der testgetriebenen Entwicklung sind:
TDD ist ein effektiver Weg, um Ihre automatisierte Test-Coverage zur Unterstützung Ihres CI/CD-Prozesses aufzubauen. TDD ist jedoch keine Voraussetzung für eine effektive DevOps-Strategie, und Sie können ein hohes Maß an automatisierter Test-Coverage auch ohne TDD beibehalten.
Mit der Durchführung automatisierter Tests im Rahmen eines CI/CD-Workflows verfolgen wir das Ziel, schnelles Feedback zu den gerade vorgenommenen Änderungen zu erhalten. Auf dieses Feedback zu hören und darauf zu reagieren, ist ein wesentlicher Teil des Prozesses. Die folgenden Best Practices helfen Ihnen, das Beste aus Ihrem automatisierten Testsystem herauszuholen:
Tools und Practices sind jedoch wie immer nur eine Seite der Medaille. Ein wirklich guter CI/CD-Automatisierungs-Workflow erfordert eine Teamkultur, die nicht nur den Wert automatisierter CI/CD-Tests erkennt, sondern auch den Stellenwert einer schnellen Reaktion auf fehlgeschlagene Tests, damit die Software jederzeit zum Deployment bereit ist.
Für viele Teams ist der Ausgangspunkt für automatisierte Tests eine Suite von Unit-Tests, die Sie manuell oder als Teil einer einfachen kontinuierlichen Integrationspipeline auslösen können. Wenn Ihre DevOps-Kultur ausgereift ist, können Sie sich in der Testpyramide nach oben arbeiten, indem Sie Integrationstests, End-to-End-Tests, Sicherheitstests, Leistungstests und mehr hinzufügen.
Kontinuierliches Testen bezieht sich auf die Praxis, eine ganze Reihe automatisierter Tests als Teil einer CI/CD-Pipeline durchzuführen. Beim kontinuierlichen Testen durchläuft jede Codeänderung automatisch eine Reihe von automatisierten Tests, so dass etwaige Fehler so schnell wie möglich entdeckt werden.
In den frühen Phasen eines kontinuierlichen Testprozesses können Tests in der IDE durchgeführt werden, bevor die Änderungen überhaupt übertragen werden. Für spätere Tests erfordert kontinuierliches Testen in der Regel Testumgebungen, die als Teil der Pipeline automatisch erneuert werden.
Ein vollständig automatisierter kontinuierlicher Testprozess bietet ein Höchstmaß an Vertrauen in Ihre Codeänderungen und beschleunigt gleichzeitig die Releases. Indem Sie Ihre Software einem strengen Testsystem unterziehen, reduzieren Sie das Risiko von Fehlern erheblich. Wenn dieser Prozess automatisch und kontinuierlich abläuft, können Sie nicht nur effizienter arbeiten, sondern auch dringende Fehlerbehebungen schnell und sicher durchführen.
Auch wenn die Implementierung von kontinuierlichen Tests einige Zeit in Anspruch nimmt, ist dies ein Ziel, auf das Sie schrittweise hinarbeiten können, während Sie andere Aspekte Ihrer CI/CD-Pipelines automatisieren und Ihre Test-Coverage ausbauen.
Ein häufiges Missverständnis unter CI/CD-Neulingen ist, dass die Testautomatisierung manuelle Tests und professionelle QA-Teams überflüssig macht.
Die CI/CD-Automatisierung spart den QA-Teams zwar etwas Zeit, macht sie jedoch nicht überflüssig. Statt sich in Routineaufgaben zu verfangen, kann sich das Testteam darauf konzentrieren, Testfälle zu definieren, automatisierte Tests zu schreiben und beim explorativen Testen Kreativität und Einfallsreichtum zu zeigen.
Im Gegensatz zu automatisierten Build-Tests, die zur Ausführung durch einen Computer sorgfältig geskriptet werden müssen, erfordern explorative Tests nur eine lose Aufgabendefinition. Der Wert von explorativen Tests besteht darin, Dinge zu finden, die bei geplanten, strukturierten Tests durch das Netz fallen. Im Wesentlichen sucht man dabei nach Problemen, für die noch kein Testfall geschrieben wurde.
Bei der Auswahl der zu explorierenden Bereiche sind sowohl neue Features zu berücksichtigen als auch Systembereiche, die bei einem Problem im Produktionseinsatz den größten Schaden verursachen würden. Um die Zeit des Testteams effizient zu nutzen, sollten manuelle Tests erst durchgeführt werden, nachdem alle automatisierten Tests bestanden wurden.
Beim explorativen Testen sollte man nicht in manuelle, sich wiederholende Tests hineinrutschen. Das Ziel ist nicht, jedes Mal die gleichen Tests durchzuführen. Wenn bei explorativen Tests Probleme entdeckt werden, müssen Sie sowohl das Problem beheben als auch einen oder mehrere automatisierte Tests schreiben. Wenn das Problem erneut auftritt, wird es auf diese Weise viel früher erkannt.
Allerdings ist der Aufbau einer Testsuite kein Unterfangen, das man abschließt und dann zu den Akten legt. Automatisierte Tests müssen gepflegt werden, um sicherzustellen, dass sie relevant und nützlich bleiben. Genauso wie Sie Ihren Code kontinuierlich verbessern, müssen Sie auch Ihre Tests kontinuierlich verbessern.
Wenn Sie Ihre automatisierten Tests auf neue Features erweitern und die Ergebnisse explorativer Tests einfließen lassen, bleibt Ihre Testsuite effektiv und effizient. Es lohnt sich auch, die Performance der Tests zu analysieren und sich zu fragen, ob es sich lohnen könnte, die Prozessschritte umzustellen oder weiter aufzuteilen, um schneller Feedback zu erhalten.
CI-Tools stellen verschiedene Kennzahlen für eine Optimierung der Pipeline bereit. „Flaky“-Indikatoren für unzuverlässige Tests wiederum können uns vor fehlgeleiteten positiven oder negativen Einschätzungen bewahren.
Kennzahlen im Auge zu behalten kann zwar hilfreich sein, um den automatisierten Testprozess zu verbessern, aber hüten Sie sich vor dem Trugschluss, dass Test-Coverage an sich das Ziel ist. Das eigentliche Ziel lautet, Ihrer Benutzergemeinde regelmäßig ein funktionierendes Softwareprodukt zur Verfügung zu stellen. Die Automatisierung dient diesem Ziel, indem sie schnelles und zuverlässiges Feedback liefert, damit Sie Ihre Software zuversichtlich für die Produktion freigeben können.
Testautomatisierung spielen in jeder CI/CD-Pipeline eine zentrale Rolle. Die automatische Durchführung von Tests liefert schnelles und zuverlässiges Feedback zu Ihren Codeänderungen. Dies wiederum macht die Entwicklung effizienter, da Fehler früher erkannt und leichter behoben werden können.
Es empfiehlt sich, Ihre automatisierten Tests nach der Dauer ihrer Ausführung zu ordnen. Unit-Tests sollten zuerst durchgeführt werden, da sie das schnellste Feedback liefern, gefolgt von Integrationstests und schließlich End-to-End-Tests. Wenn Sie noch keine automatisierten Tests haben, sind Unit-Tests der beste Anfang. Die testgetriebene Entwicklung (TDD) ist eine bewährte Entwicklungspraxis, die Ihnen helfen kann, die Test-Coverage zu verbessern und zu erhalten.
Wenn Ihre DevOps-Kultur sich weiterentwickelt, möchten Sie vielleicht zu kontinuierlichen Tests wechseln. Zu diesem Übergang gehört auch die Automatisierung der Erstellung und Pflege Ihrer Testumgebungen. Wenn Sie automatisierte Tests auf höherer Ebene schreiben, sollten Sie die Bereiche mit dem größten Risiko priorisieren. Dies kann automatisierte Leistungstests wie Last-, Stress- oder Soak-Tests erfordern. Manuelle explorative Tests sind eine gute Möglichkeit, Lücken in der Test-Coverage zu identifizieren, damit Sie Ihren CI/CD-Prozess weiter verbessern können.
TeamCity bietet umfangreiche Unterstützung für Test-Frameworks und eine Reihe von Testautomatisierungsfunktionen, die Ihnen helfen, das Beste aus Ihrem CI/CD-Prozess herauszuholen.
Geschwindigkeit und Zuverlässigkeit sind für eine effektive Testautomatisierung unerlässlich, und TeamCity ist für beides optimiert. TeamCity stellt nicht nur detaillierte Testberichte zur Verfügung, damit Sie Problemen schnell auf den Grund gehen können, sondern hebt auch automatisch fehlerhafte Tests hervor, damit Sie sicherstellen können, dass nur gültige Fehler markiert werden. Intelligente Reihenfolgenänderung und Parallelisierung für Tests liefern noch schnellere Ergebnisse, und die Remote-Run-Funktion liefert Feedback, bevor Sie die Tests festschreiben.
Da TeamCity Integrationen mit Issue-Trackern, IDEs, Slack und anderen Plattformen bietet, können Sie Benachrichtigungen über fehlgeschlagene Tests erhalten, egal wo Sie gerade arbeiten. Die vollständige Unterstützung für virtuelle Maschinen und Docker-Container ermöglicht es Ihnen, die Verwaltung Ihrer Testumgebungen zu automatisieren und kontinuierliche Tests als Teil Ihres CI/CD-Prozesses zu implementieren.