Eine höhere Geschwindigkeit bei der Software-Entwicklung darf den Qualitätsaspekt nicht ausschließlich auf den Entwickler oder die Softwaretester abwälzen. Warum hier die Testpyramide der Software-Entwicklung ins Spiel kommt, lesen Sie in unserem Gastbeitrag von leogistics, spezialisiert auf die Software-Entwicklung im SAP- und Logistik-Umfeld.
In den vergangenen Jahren sind Projekte zunehmend agil geworden. Das heißt, dass längere Implementierungsphasen von sechs bis 18 Monaten in kleinere Releases aufgeteilt werden, die nicht länger als zwei Monate dauern. Oftmals wird auch im Wochenrhythmus geplant. So sind bei uns in der Entwicklung unserer Logistikplattform myleo / dsc Releases alle zwei Wochen angesetzt.
Im Projektgeschäft entwickelt sich der Scope auch im minutiös durchgeplanten Wasserfall ständig. Über die Gründe lässt sich spekulieren – mangelnder Buy-in der Fachabteilungen zum Projektbeginn, zu wenig Zeit in der Anforderungsanalyse, wechselnde Verfügbarkeit von Schlüsselwissensträger:innen – aber das Symptom ist evident und sehr gut bekannt.
Diese Vorteile bringen schnelle Iterationen
Schnellere Release-Zyklen bringen viele Vorteile mit sich. Man muss dazu nicht mit schwammigen Aussagen wie einer erhöhten Kundenzufriedenheit argumentieren, denn präzise formulierte Argumente finden sich zuhauf:
- So sind etwa schnellere Iterationen und Anpassungen am Projektplan möglich und das Risiko einer Fehlsteuerung des Scope wird minimiert.
- Außerdem können Features einzeln bereitgestellt werden und Anwender:innen müssen nicht lange auf ein Major Release warten. Dies zahlt sich vor allem dann aus, wenn die Anwendung bereits in Produktion läuft.
- Ein anderer, auf die Entwicklungsorganisation gerichteter Aspekt betrifft die Schnelligkeit der Entwickler und die Sicherheit, dass Features auch nach einem Release noch einwandfrei funktionieren. Wenn ein Release oft (aus unserer Sicht mindestens alle vier Wochen) ausgerollt wird, werden Fehler, wenn sie in der Produktion auftreten, schneller behoben. Dies liegt daran, dass echte oder vermeintliche Fehler schneller einer in der Vergangenheit implementierten Änderung zugeordnet werden können. Ist das fehlerhafte Feature schnell identifiziert, kann auch die fehlerhafte Stelle im Quellcode schneller gefunden werden. Es ist wie im echten Leben: Wenn man gerade seinen Schlüssel sucht – weiß man, wo und wann man ihn noch zuletzt in der Hand hatte, lassen sich die Orte, wo er jetzt liegen könnte, schnell eingrenzen.
Schöne neue, schnelle Welt?
Schnelle Releases bringen also eine Reihe von Vorteilen, haben aber auch ihre Schattenseiten. Schließlich bedeuten sie vor allem eins – viele, oft durchgeführte, kleine Änderungen. Dies bringt mit sich, dass spätestens nach jedem Release getestet werden muss. Aus kaufmännischer Sicht ist die Frage durchaus nicht unerheblich, wer wie viel und was testen muss.
Bei einer SaaS-Lösung (Software as a Service) ist die Sache klar – der Hersteller muss den Löwenanteil übernehmen, gibt er doch den Release-Zyklus vor. In der Produktentwicklung und im Projektgeschäft nehmen Tests ebenfalls eine wichtige Rolle ein – sind sie doch die einzige Möglichkeit, zu verifizieren, ob die Software genau das tut, was von ihr erwartet wird.
Im Folgenden beziehe ich mich jedoch vor allem auf SaaS-Lösungen, wobei die Aspekte durchaus auch ihre Gültigkeit haben, wenn ein Implementierungsprojekt läuft.
Die Testpyramide aus leogistics-Perspektive
Zunächst müssen wir die Begriffe „Entwicklertest“ und „Benutzertest“ beiseitelegen, denn diese sind zu unscharf formuliert. In der Produktentwicklung gibt es die folgenden Tests. Hierbei sei die Testpyramide genannt, die bekannt sein dürfte und Industriestandard ist. Einige Testarten habe ich der Übersicht halber zusammengefasst.
Der Begriff „Pyramide“ bezieht sich wohl eher auf die Anzahl der Tests (links), statt auf die Testabdeckung (rechts). Puristen mögen einwenden, dass auch die Testabdeckung eine Pyramide sein sollte, lassen in dieser Betrachtung aber allzu oft die Budgetsituation außer Acht. In der Realität dürfte die Testabdeckung (gemessen sowohl an der Code- als auch der Zweigabdeckung) eher ein Pfahl sein, mit verjüngtem Fundament und verjüngter Spitze. Aber auch damit kann man schon recht weit kommen.
Die Stoßrichtung der reinen Lehre und der Praxis ist aber dieselbe: So viel wie möglich automatisieren, denn automatisierte Tests sind auf lange Sicht günstiger als manuelle Tests.
- Unit Tests testen einzelne Methoden und sonst nichts. „Fremde“ Methoden müssen isoliert werden und reproduzierbare Outputs liefern (Stub/Fake) oder Inputs reproduzierbar validieren (Mock, Spy). Gängige Tools aus unserer Praxis sind ABAP Unit Tests oder Jest im Node.js-Umfeld. Der Aufwand korreliert sehr oft mit der Anzahl der zu erstellenden Test Doubles. Tests mit mehr als drei Test Doubles sind erfahrungsgemäß schwerer lesbar, wodurch der Mehrwert durch erhöhten Pflegeaufwand schnell sinken kann. Dann ist es besser, sich eine Ebene weiter nach oben zu hangeln.
- Component / Service Tests testen den „Durchstich“ einer API von oben bis zur Datenbank, aber keine fremden Module oder Services. Daher sind auch hier Test Doubles notwendig. Während wir es bei Unit Tests noch mit einem Skalpell zu tun hatten, arbeiten wir hier bereits mit einer Schere – die Abdeckung steigt, aber Fehler lassen sich ungenauer eingrenzen. Der Ausgangszustand eines jeden Tests wird hier nicht über Test Doubles erreicht, sondern über ein definiertes Setup in der Datenbank – entweder in der physischen Datenbank, abgegrenzt in einem separaten Mandanten (bei SAP-Systemen), oder über eine In-Memory-Datenbank, die eigens für den Test gestartet wurde (z. B. https://www.npmjs.com/package/mongodb-memory-server). Wichtig ist hierbei zu erwähnen, dass die Lösung mit einer lokalen In-Memory-DB meist parallelisierbar ist, also auch Pipeline-fähig ist, während ein eigener Testmandant in SAP nicht ohne größere Aufwände parallelisierbar ist, da es pro Testlauf kein eigenes Stammdaten-Setup gibt und schon gar keinen eigenen Mandanten. Hier empfiehlt sich also eine nächtliche, serielle Testausführung, statt einer parallelen, Race-Condition-behafteten Testausführung.
Gängige Tools aus unserer Praxis sind ABAP Unit Tests + leogistics-eigener Oberbau, sowie Supertest / Jest im Node.js-Umfeld.
- GUI / Integrations / API Tests testen die Softwarelösung, aber nicht zwangsläufig andere Systeme, die angebunden sind. Die Tests sind automatisiert und setzen direkt auf dem UI oder der API auf. Test Doubles sind überschaubar und beschränken sich darauf, eingehende Nachrichten zu simulieren, oder ausgehende Aktionen zu verifizieren (z. B. https://www.inbucket.org/). Diese Tests sind, falls sie auf einem isolierten Stammdaten-Setup aufsetzen können, parallelisierbar. Ansonsten sind sie Kandidaten für einen seriellen, periodischen Testlauf. Gängige Tools aus unserer Praxis sind eCATT oder Cypress. Übrigens haben wir es hier nicht mehr mit einem Skalpell oder einer Schere zu tun, sondern mit einem Rasenmäher – also einer hohen Abdeckung, aber im Zweifel langwierigerer Fehlersuche, falls tatsächlich ein Fehler entdeckt wird.
- Manuelle Tests – hier siegt der Faktor Mensch. Manuelle Tests sind unerlässlich, aber in jedem Falle teuer und sind daher sparsam und durchdacht einzusetzen. Wenn repetitive Testfälle identifiziert wurden, sollte man sich in der Pyramide weiter unten umschauen.
Praxisbeispiel
In der Entwicklung der myleo / dsc sind Unit- und Servicetests fest in die Gitlab-Pipelines integriert – wann immer ein Commit auf einem Remote-Branch stattfindet, laufen die Tests durch. Diese sind parallelisierbar und stören einander nicht. Erst wenn es grünes Licht gibt, darf auf ein Merge auf den Develop-Branch stattfinden, auf dem seinerseits die Tests nochmal durchlaufen werden.
Auf der ersten Integrationsumgebung finden nächtlich die GUI- / Integrations- und API-Tests statt. Diese sind im Einzelfall zwar parallelisierbar, aber dauern für die Pipelines oft zu lange (gerne ein bis zwei Stunden pro Modul), da auch das UI mit getestet wird. Treten nachts Fehler auf, sind diese morgens im Schedule-Report von Gitlab sichtbar.
Alle zwei Wochen wird ein Deployment auf die Preproduction-Umgebung durchgeführt. Die Tests werden hier erneut durchgeführt. Erst wenn alle Fehler behoben wurden, darf das Release auf die Produktionsumgebung gebracht werden.
Manuelle Tests spielen in diesem Prozess eine gewichtige Rolle – aber eher aus UX- und fachlicher Sicht, weniger zur Absicherung gegen Regressionen.
Fazit
Als im 19. Jahrhundert die Eisenbahn erfunden wurde und etwas später das Automobil, wurde der Geschwindigkeitszuwachs durch technische Sicherungsmaßnahmen flankiert. Der Faktor Mensch wurde im Vergleich zur technischen Entwicklung von Karosserie, Anschnallgurten, Airbags, ABS und ESP weniger stark beachtet. Vergleichbar ist dies auch mit der Software-Entwicklung: Höhere Entwicklungsgeschwindigkeit darf den Qualitätsaspekt nicht ausschließlich auf den Entwickler oder Software-Tester abwälzen. Auch hier sind reproduzierbare, technische Sicherungsmaßnahmen notwendig, die systematisch in die Qualitätssicherung eingebunden sind.