Moderne Testprozesse folgen dem Ansatz, dass sie in den Entwicklungsprozess eingebettet sind und von der ersten Planungsphase an diesen wesentlich mitgestalten und unterstützen [Pol00]. Vorbei sind die Zeiten, in denen versucht wird, am Ende der Entwicklung Qualität nachträglich zu integrieren. Gerade agile Ansätze schreiben sich selbst auf die Fahne, einen kompletten Entwicklungsprozessdurchlauf in einer sehr kurzen Zeitspanne, der sogenannten Iteration, komplett durchzuexerzieren und am Ende jeder Iteration potenziell ein weiterentwickeltes Produkt ausliefern zu können (vgl. u. a. [ScrumP]).
Problemstellung
Aber was ist mit der Qualität, die durch den Testprozess sichergestellt wird? Durch die Einbettung in den Entwicklungsprozess müssen folglich auch dort die Geschwindigkeit sowie die Bereitschaft, Änderungen positiv gegenüberzustehen, um einiges gesteigert werden. Sind nur wenige Tage für die gesamte Entwicklung neuer Funktionalitäten pro Iteration verfügbar, verbleiben noch weniger Tage für Verifikation und Validierung übrig. Es bedarf also geeigneter Methoden, damit die Rückmeldung aus dem Testprozess, nämlich ob die Qualität den definierten Parametern entspricht, zum richtigen Zeitpunkt vorhanden ist.
In diesem Kontext hat sich die Testautomatisierung als Mittel der Wahl etabliert [CTR20]. Aus gutem Grund: Im Vergleich zum manuell durchgeführten Test sind die Ergebnisse konsistenter, reproduzierbarer und auch wesentlich schneller verfügbar [ISTQB]. Die Einbettung des Testprozesses folgt dabei dem Prinzip der Kontinuität: Genauso wie die Software kontinuierlich gebaut und zu einem Gesamtprodukt integriert wird (Continuous Integration), werden auch die automatisierten Tests kontinuierlich für jede einzelne Änderung an der Software durchgeführt (Continuous Testing).
Zur Sicherstellung einer fortlaufenden und gleichbleibenden Qualität sieht der Testprozess Quality Gates als Wächter vor (vgl. Abbildung 1): Liegen nicht alle Qualitätsmerkmale der überprüften Änderung innerhalb der geforderten Parameter, hält der kontinuierliche Prozess inne und es muss nachgebessert werden. Diese Überprüfung findet vielmals parallel für jede Änderung statt.
Abb. 1: Quality Gate innerhalb einer kontinuierlichen Softwareintegration
Die zuletzt genannte Tatsache birgt jedoch auch zwei, oftmals zu Beginn der Prozessetablierung unterschätzte Gefahren, die sich mit zunehmender Anzahl an Tests immer weiter verschärfen:
- die benötigte Rechenkapazität zur Durchführung aller Quality Gates sowie
- deren maximale Durchlaufzeit.
Die mögliche Bandbreite dieser Problematik ist nur zu gut bekannt: Was bringen Agilität und ein auf Kontinuität ausgelegter Prozess, wenn die Regressionstests am Quality Gate Stunden oder sogar mehrere Tage dauern? Der gesamte Prozess gerät ins Stocken und die Qualität wird „später“ überprüft. Selbst wenn nur relevante Tests am Quality Gate ausgeführt werden würden, bleibt die Frage offen, wer auf welcher Basis entscheidet, welche Tests überhaupt relevant sind. Und wer trägt dafür die Verantwortung, bei einer sehr hohen Änderungsrate der Software inkl. deren Abhängigkeiten und Kommunikationsstrukturen auch die Quality Gates kontinuierlich anzupassen?
Der nachfolgend beschriebene Ansatz nutzt die Möglichkeiten der künstlichen Intelligenz, damit Quality Gates kontinuierlich, automatisch und basierend auf der aktuellen Änderung selbstständig entscheiden, welche Tests aufgrund ihrer Fehlerwahrscheinlichkeit auszuführen sind. Damit erhöht sich im Vergleich zur vollständigen Regression nur minimal das Risiko, Fehler nicht zu entdecken, aber die Durchlaufzeit sowie die benötigte Rechenkapazität können signifikant gesenkt werden.
Lösungsansatz
Die Lösung dieses Problems liegt in der Entwicklung einer intelligenten Verarbeitungssequenz (Pipeline), die eine möglichst minimale Teilmenge an Tests bestimmt, welche mit hoher Wahrscheinlichkeit fehlschlagen werden (vgl. Abbildung 2). Diese Wahrscheinlichkeit wird mithilfe eines Modells des maschinellen Lernens bestimmt, das unter Verwendung von historischen Daten trainiert wird. Die dafür benötigten Daten stammen aus verschiedenen, sehr unterschiedlichen Quellen. Denkbar sind zum Beispiel Issueoder Bug-Tracker, Quellcodeverwaltung, Dokumentation, (statische) Quellcode-Analyse oder Automatisierungsserver.
Abb. 2: Schematische Darstellung der beschriebenen Pipeline
Bevor das eigentliche Modell trainiert wird, werden zuerst alle verfügbaren Daten in ein gemeinsames, vereinheitlichtes und abstraktes Datenmodell, das Unified Data Model (UDM), überführt. Dieses dient dann als Schnittstelle zwischen den quellenspezifischen Rohdaten und dem Feature Engineering sowie den Algorithmen beziehungsweise Modellen des maschinellen Lernens.
Die zum Trainieren solcher Modelle benötigten Merkmale (Features) werden direkt vom UDM bereitgestellt. Dabei handelt es sich zum einen um die Rohdaten, die ohne weitere Verarbeitung als Merkmale verwendet werden können. Zum anderen sind es Merkmale, die Codierungsverfahren oder ein komplexeres Feature Engineering benötigen. Das UDM stellt darüber hinaus auch sicher, dass alle fehlenden Werte in den Daten sinnvoll gefüllt werden.
Auf Basis dieser Merkmale wird nun die Zeit, die für das Testen von Software benötigt wird und die ein Entwickler auf Rückmeldung wartet, verringert. Zur Lösung dieses Problems ergeben sich zwei konkrete Ziele.
Das erste Ziel bestimmt eine optimale Teilmenge an Tests, welche nur diejenigen Tests beinhaltet, die im Zusammenhang mit der durchgeführten Änderung fehlschlagen können. Der bevorzugte Ansatz liegt dabei in der Analyse jedes einzelnen Tests und der Zuweisung eines Wertes zwischen 0 und 1, der die Wahrscheinlichkeit beziehungsweise das Risiko eines Fehlschlags darstellt.
Das zweite Ziel ist die Priorisierung dieser Teilmenge, um den Entwicklern möglichst schnell Rückmeldung zu geben. Hierfür wird das gleiche Vorgehen wie zur Lösung des ersten Ziels verwendet und die Tests werden nach dem Risiko eines Fehlschlags sortiert.
Der erste Prototyp
Um den generellen Lösungsansatz und im Besonderen das UDM zu validieren, wurde ein erster Prototyp entwickelt [Schw21]. Bei diesem lag der Fokus zunächst auf der Bestimmung einer optimalen Teilmenge an Tests. Auf die Priorisierung der Tests wurde vorerst noch verzichtet. Als Grundlage dienen Daten, die aus zwei großen Open-Source-Projekten, PyTorch [Pas19] und Chromium [CHROMIUM], gesammelt wurden. Ebenso wurde für die ersten Untersuchungen ein Entscheidungsbaum, ein vergleichsweise einfacher Algorithmus des maschinellen Lernens, verwendet. Dieser bietet den Vorteil, dass jederzeit ein Blick auf die interne Struktur möglich ist. Somit ist ein besseres Verständnis der zugrunde liegenden Daten und Merkmale gewährleistet.
Zur Bewertung werden die Qualitätsmetriken Precision und Recall verwendet. Da diese Metriken falsch-positive und falsch-negative Ergebnisse nutzen, muss zunächst definiert werden, was die Positiven und Negativen sind. Im betrachteten Fall sind die Testfehlschläge die gesuchte Zielgröße und werden daher als Positive betrachtet. Daraus folgt, dass erfolgreiche Tests als Negative anzusehen sind.
Die Precision ist ein Maß für falsch-positive Ergebnisse. Das bedeutet, dass ein erfolgreicher Test als fehlgeschlagener Test vorhergesagt wird. Eine niedrige Precision führt zu einer ungünstigen Teilmenge an Tests, die noch viele erfolgreiche Tests enthält. Der Recall hingegen ist ein Maß für die falsch-negativen Ergebnisse. Falsch-negative Ergebnisse sind fehlgeschlagene Tests, die als erfolgreich vorhergesagt wurden. Ein niedriger Recall führt zu dem Problem, dass viele fehlgeschlagene Tests nicht als solche vorhergesagt werden. Da das Ziel darin liegt, eine Teilmenge an Tests zu bestimmen, die mit hoher Wahrscheinlichkeit fehlschlagen werden, liegt der Fokus darauf, den Recall so weit wie möglich zu verbessern, ohne dabei die Precision zu stark zu verringern.
Aufgrund der riesigen Menge gesammelter Daten wurden zunächst für die Entwicklung einige Einschränkungen vorgenommen. Neben der Verwendung einer Teilmenge an Daten wurden einzelne Tests, die thematisch zusammengehören, zu Testsuites zusammengefasst. Auch die Änderungen am Code werden nicht für einzelne Dateien, sondern auf der Ebene von Commits, die Änderungen an mehreren Dateien beinhalten können, betrachtet.
Abbildung 3 zeigt die Ergebnisse des ersten Prototyps. Hier wurden alle direkt in den Rohdaten vorhandenen sowie einige komplexe und codierte Merkmale benutzt. So sind die Anzahl an hinzugefügten, geänderten und entfernten Codezeilen oder die Anzahl der Autoren als Beispiele für direkt vorhandene Merkmale zu nennen. Wohingegen die Anzahl der Fehlschläge eines Tests in den letzten Durchläufen als Beispiel für ein komplexes Merkmal steht.
Abb. 3: Ergebnisse des ersten Prototyps
Die Ergebnisse stammen aus einer 10-fachen Kreuzvalidierung. Die Genauigkeit liegt zwar nahe bei 100 Prozent, dies war aber aufgrund des Ungleichgewichts in den Daten zu erwarten: Der Anteil an erfolgreichen Tests ist deutlich höher als der Anteil der fehlgeschlagenen. Bei einem solchen Ungleichgewicht ist der Wert der Genauigkeit (Accuracy) als Qualitätsmerkmal eher ungeeignet. Er zeigt lediglich an, ob das Modell ausschließlich rät oder wie im vorliegenden Fall tatsächlich lernt.
Die Metriken Recall und Precision sind in diesem Fall aussagekräftiger. Selbst mit einer Auswahl simpler Merkmale wird schon ein Recall von ca. 66 Prozent erreicht. Das bedeutet, dass nur ein Drittel der fehlschlagenden Tests nicht erkannt wird. Die Precision von ca. 65 Prozent bedeutet, dass es sich bei zwei Drittel der Tests in der vom Modell bestimmten Teilmenge tatsächlich um fehlschlagende Tests handelt.
Fazit
Der Prototyp zeigt, dass das vorgestellte UDM funktioniert. Die Daten aus verschiedenen Softwareprojekten können damit vereinheitlicht werden, was am Beispiel von zwei Open-Source-Projekten gezeigt wurde. Damit ist es möglich, die weitere Datenverarbeitung und das Feature Engineering unabhängig von den betrachteten Projekten durchzuführen. Auch der verwendete Algorithmus des maschinellen Lernens kann ohne aufwendige Anpassungen ausgetauscht werden. Dies macht es wesentlich einfacher, die Leistung verschiedener Algorithmen zu vergleichen und den am besten geeigneten auszuwählen.
Der Prototyp würde im jetzigen Zustand bereits zu einer deutlichen Verringerung der Durchlaufzeit sowie der benötigten Rechenkapazität führen. Wendet man die Vorhersage auf die Beispieldaten an, müssten anstatt alle 259.370 Tests nur noch 5.760 durchgeführt werden. Dies entspricht einer Ersparnis von ca. 98 Prozent, wobei hier die Zeit, die einzelne Tests benötigen, noch nicht berücksichtigt ist. Bevor das System produktiv eingesetzt werden kann, muss allerdings noch der Recall verbessert und damit auch das Risiko, fehlschlagende Tests nicht vorherzusagen, minimiert werden.
Weitere Informationen
[CHROMIUM]
https://www.chromium.org/Home
[CTR20] Capgemini, Sogeti, Continuous Testing Report 2020, siehe:
https://www.capgemini.com/de-de/wp-content/uploads/sites/5/2020/03/Report_Continous_Testing_2020_Sogeti_Capgemini.pdf
[ISTQB] International Software Testing Qualifications Board, Certified Tester Advanced Level Syllabus: Test Automation Engineer, siehe:
https://www.istqb.org/downloads.html
[Pas19] A. Paszke et al., Pytorch: An Imperative Style, High-Performance Deep Learning Library, in: Advances in Neural Information Processing Systems 32, pp. 8024-8035, 2019
[Pol00] M. Pol, T. Koomen, A. Spillner, Management und Optimierung des Testprozesses: ein praktischer Leitfaden für Testen von Software, mit TPI und TMap, dpunkt.verlag, 2000
[Schw21] A. Schwarz, B. Morgenthaler, V. Vaquero Martinez, M. Garrido García, M. Duque-Antón, Verify Embedded Systems Faster and more Efficiently with Artificial Intelligence, in: Conf. Proc. of Upper Rhine Artificial Intelligence Symposium 2021
[ScrumP] P. Deemer, G. Benefield, C. Larman, B. Vodde, The Scrum Primer: Leitfaden zur Theorie und Anwendung von Scrum, siehe:
https://scrumprimer.org/primers/de_scrumprimer20.pdf