DevOps und Modellbasiertes Testen als Effizienz-Turbo
Testing per Knopfdruck
Die meisten Unternehmen haben mittlerweile erkannt, dass sich durch Vereinfachung und Automatisierung der Abläufe in Softwareentwicklung und Deployment viele Vorteile erzielen lassen. Die Effizienz kann deutlich gesteigert werden, die Time-To-Market und Release-Zyklen werden immer weiter verkürzt, die Häufigkeit von Fehlern wird stark reduziert und nicht zuletzt haben die Mitarbeiter mehr Spaß bei der Arbeit. Die Liste ließe sich noch lange fortsetzen. Zusammengefasst wird dieses Phänomen unter dem Namen „DevOps“.
Im Bereich der Qualitätssicherung, insbesondere im Testdesign, klafft derzeit aber noch eine Lücke in Bezug auf die Automatisierung. Auch im „DevOps“-Prozess werden Testfälle noch von Hand entworfen und implementiert. Zwar werden sie in der Regel automatisch durchgeführt, oft sind sie aber nicht aktuell, sondern hinken der Entwicklung hinterher.
Die erstellten Testfälle finden dann üblicher Weise erst in der darauffolgenden Testphase Verwendung. Dem heute eigentlich obligatorischen DevOps-Ansatz entspricht diese Vorgehensweise allerdings bei weitem nicht. Mit DevOps sollte es keinen zeitlichen Versatz zwischen aktuellem Entwicklungsstand und Stand der Testspezifikationen geben. Auch sollte es keine vom Rhythmus der Entwicklung unabhängigen Testphasen geben: Alle Vorgänge sollten nahtlos ineinandergreifen, am besten per Knopfdruck. Alles ist auf dem gleichen Stand, alles ist zusammenhängend gespeichert, es gibt einen „Single Point Of Truth“!
Mit dem modellbasierten Ansatz steht hierfür schon seit geraumer Zeit eine Methode zur Verfügung, auch die Testfallerstellung zu automatisieren und in einen automatischen Build-Prozess einzubinden. Bei diesem Ansatz werden graphische Modelle verwendet, um das Testvorgehen zu unterstützen. Ähnlich wie im Behaviour Driven Development werden die Erwartungen an das zu entwickelnde System mit sämtlichen relevanten Szenarien in einem Ablaufmodell beschrieben. Die graphische Notationsform der Modelle erlaubt es, bei der Erstellung der Anwendungsszenarien auch den Product Owner, Requirement Engineers und Entwickler mit einzubeziehen und somit einen Mehrwert nicht nur für den Test zu schaffen. Die Modelle dienen also nicht nur der Definition des Testvorgehens, sondern auch als Input für die Entwickler und als Kommunikationsmedium im gesamten Entwicklungsprozess.
Abb. 1: vom Modell zum Testfall
Die automatische Generierung der Testfälle aus dem Modell kann entweder manuell angestoßen werden, oder – eine geeignete Tool-Kette vorausgesetzt – in den automatisch ablaufenden CI Prozess integriert werden. Aus Sicht der Qualitätssicherung bietet sich damit die Möglichkeit, die Tests synchron mit der Entwicklung zu halten, da die Testmodelle ohne großen Aufwand parallel zum Fortschritt in der Entwicklung weiterentwickelt werden können.
Dargestellt ist dieses Verfahren in Abb. 2. Tester und Entwickler sitzen im selben Boot – Modell und Code werden passend zueinander in das Repository geschrieben und automatisch vom CI-Server weiterverarbeitet: Der Code wird gebaut, die Testfälle werden automatisch (aus dem Modell) generiert und die Tests werden ausgeführt.
Abb. 2: Einbindung des Testfallgenerators in die Automatisierungs-Toolchain
sepp.med hat diesen Ansatz in seiner CI Tool-Kette umgesetzt. Beginnend mit der Übernahme der einzelnen Tasks aus dem Backlog werden parallel zur Entwicklung Testmodelle erstellt und weitergepflegt, wobei synchron zur Entwicklung Testmodelle zu den aktuellen neuen Features entstehen bzw. Änderungen Eingang in das Testdesign finden. Damit und mit der automatischen Testfallgenerierung aus den Modellen ist es dann möglich, auch im CI-Ablauf die Testfälle automatisch zu erstellen bzw. anzupassen und auszuführen. Die MBTsuite, das modellbasierte Testingframework der sepp.med gmbh, kann seit einiger Zeit in die DevOps Toolchains integriert werden und schließt so die Lücken der Testfallerstellung in der DevOps-Welt.
Die Dienste werden durch ein API zur Verfügung gestellt. Dies ermöglicht die Einbindung des Testfallgenerators in Automatisierungs-Toolchains, zum Beispiel in einen Continuous Integration- bzw. Continuous Deployment Prozess. Durch die offene XML-RPC-Schnittstelle können die Dienste von den unterschiedlichsten Tools aus angesteuert werden. Bei sepp.med wird Jenkins eingesetzt. Durch eine kleine Anpassung in Jenkins (Beispiel-Code siehe unten) wurde die Verbindung mit der MBTsuite hergestellt.
Abb. 3: sepp.med CI-Umgebung
In Abb. 3 ist die verwendete Werkzeugkette dargestellt. Sowohl der Programmcode also auch die Testmodelle werden in ein gemeinsames git-Repository abgelegt. Im Sinne von Continuous Integration sollten dabei Programmcode und Testmodell immer auf dem gleichen Stand gehalten werden. Am Jenkins-Buildserver wird danach nicht nur der Code gebaut, sondern auch aus dem Testmodell die aktuellen Testfälle generiert. Im Anschluss werden die Testfälle automatisch ausgeführt. Bei erfolgreicher Testausführung kann ein Deployment der in Nexus abgelegten Build-Artefakte stattfinden.
Diese Vorgehensweise hat klare Vorteile. Wird beispielsweise ein neues Feature entwickelt, so wird nicht nur der geänderte Programmcode in die Integrationspipeline geschickt, sondern auch die angepassten Testmodelle. Die Arbeiten an den Testfällen und die Entwicklung der Software finden also im gleichen Rhythmus statt. Gleichzeitig wird durch das Erstellen der Testmodelle die Testbarkeit der Anforderungen sichergestellt und ein einheitliches Verständnis über die Anforderungen hergestellt.
Es müssen keine Testphasen mehr koordiniert werden und der Abgleich verschiedener Stände von Testspezifikation und Entwicklungsstand erübrigt sich. Die Generierung der Testfälle, die Testausführung und das Reporting können komplett automatisch passieren. Dies führt nicht nur zu Effizienzsteigerungen und Vereinfachungen auf technischer, sondern auch, und viel wichtiger noch, auf der Ebene zwischenmenschlicher Kooperation.
Einen wesentlichen Beitrag liefern hierzu die Testmodelle, da sie neue Features eindeutig und klar beschreiben werden und Missverständnisse zwischen den Akteuren vermieden.
Dadurch, dass Testdesigner und Entwickler den gleichen Rhythmus leben, wachsen sie näher zusammen, kommunizieren besser und kooperieren enger.
Bei sepp.med verwendet das MBTsuite-Entwicklungsteam sein eigenes Produkt, um Testfälle wie oben beschrieben automatisch zu generieren und dann auszuführen. Tester, Developer und Product Owner wirken im agilen Ablauf eng zusammen. Das Team arbeitet nach Kanban und versucht, möglichst häufig auslieferbare Stände zu erreichen. Durch die permanente Synchronisation von Produktcode und Testmodell sowie die automatische Generierung und Ausführung der Testfälle wird dieses Ziel optimal unterstützt.
Mit diesem Vorgehen konnte sepp.med die Entwicklung deutlich beschleunigen und gleichzeitig die Qualität der Produkte nachhaltig verbessern. Konkret wurde folgender CI Prozess realisiert:
Abb. 4: sepp.med CI Prozess mit Testfallgenerierung
Aus dem Ticketsystem werden die Tasks entnommen und zunächst als Testmodell umgesetzt, um die Testbarkeit sicher zu stellen und eine einheitliche Sicht auf das neue Feature beziehungsweise dessen Implikationen auf das Gesamtsystem herzustellen. Triviale Tickets könne natürlich direkt zu einem oder wenigen Testfällen umgesetzt werden. Wird der CI Prozess durch einen Commit angestoßen, werden zuerst die aktuellen Modelle verwendet um ein geeignetes Testset zu erzeugen. Dieses wird dann im „normalen“ CI Prozess genutzt.
Abschließend möchten wir anhand eines einfachen Code-Beispiels in der Skriptsprache groovy zeigen, wie die Testfallgenerierung in den CI Prozess integriert werden kann.
Zunächst wird das XML-RPC Library zur Fernsteuerung der MBTsuite in das groovy Skript importiert. Es wird benötigt, um auf den Service der MBTsuite zuzugreifen. Danach wird die Klasse RemoteController definiert, die in den weiteren Schritten mit Inhalt gefüllt wird:
Die Klasse RemoteController benötigt zwei Instanzvariablen: Die mbtsuite speichert den MBTsuite-Proxy für den Remotezugriff, während in mbtsuite Process die Referenz auf den eigentlichen MBTsuite Prozess gespeichert wird. Die MBTsuite hört zudem standardmäßig auf Port 8600 auf Requests. Deshalb wird der in den Konstruktoren erzeugte XML-RPC Proxy mit Port 8600 auf Localhost verbunden.
Außerdem soll die MBTsuite lokal auf dem Rechner gestartet werden, um dann auf ihre Dienste zuzugreifen. Die Methode startet die MBTsuite, falls nicht schon eine Instanz der MBTsuite läuft. Danach kann durch Aufruf der Methode waitForStart solange gewartet werden, bis die MBTsuite nach dem Start-Aufruf auf Anfragen reagiert. Erst dann steht der Remote-Service bereit:
Mit der Methode importModelFromFile kann das Modell in die MBTsuite geladen werden. Das API stellt dafür die Funktion importModel zur Verfügung:
Aus dem importierten Modell können dann mit verschiedenen Strategien Testfälle erzeugt werden:
Die erzeugten Testfälle können hiernach in verschiedene Zielformate und zu verschiedenen Tool exportiert werden. Wir haben uns hierfür die Methode runExport definiert:
Die Methode stop verwendet die XML-RPC Funktion close, um die MBTsuite sauber zu schließen:
Zum Schluss noch unsere main-Methode, die alle Schritte der Reihe nach aufruft. Wir starten die MBTsuite und importieren das Modell aus der Datei Bllinking.xml. Danach führen wir die FPC-Strategie aus, um die Testfälle zu generieren. Die generierten Testfälle werden in das HTML-Format exportiert und die MBTsuite wird wieder beendet:
Mit Einführung dieses CI-Vorgehens konnten wir unsere Entwicklungszyklen deutlich beschleunigen. Von vormals acht bis zehn Monaten pro Release konnte die Zeit auf zunächst sechs und mittlerweile zwei bis drei Monate verkürzt werden. Da der Test synchron zur Feature-Entwicklung gehalten ist, entfallen längere Testphasen am Ende der Entwicklungsphase.
Resumée: Die Entwickler sind glücklich, weil sie genauer wissen, was sie entwickeln sollen – die Tester sind glücklich, weil der Teststress am Ende entfällt.