Die agile Entwicklung produziert mit jedem Sprint beziehungsweise jedem Inkrement weiteren Funktionsumfang. Der Funktionsumfang der Sprints summiert sich für das Testen eines Releases somit immer weiter auf, während die Entwicklungsarbeiten für die neuen Umfänge konstant bleiben. Im Test ist immer mehr in der gleichen Sprint-Zeitdauer abzusichern beziehungsweise (weg-) zu priorisieren.
Unter der Annahme, dass Funktionen und Charakteristiken der vergangenen Sprints auch für zukünftige Releases erhalten bleiben sollen, führt dies zu einem Regressionstest-Vorgehen, welches mit jedem Sprint um neue Umfänge ergänzt werden müsste.
Somit benötigt es eine deterministische und skalierte Testausführung, um dieses „konstante Absicherungsumfangswachstum“ in einem Sprintzeitfenster bewältigen zu können. Um dieses agile Arbeiten zu unterstützen, werden im Folgenden die Konzepte hinter einem dafür geschaffenen Testing-Service vorgestellt.
Ausgangskonzept
Prinzipiell ist es sinnvoll, wo immer es möglich ist, Entwicklungsansätze auf das Testen zu übertragen – dies etabliert auch eine gemeinsame Sprache und ein gemeinsames Verständnis in den Teams aus Dev-, Test-, Ops-Experten usw.
Immutable Infrastructure ist eine Methodik, die in der DevOps- und Cloud-Welt etabliert ist. Der Ansatz beruht darauf, dass IT-Infrastruktur nicht mehr „händisch gepflegt“ wird. Dies wird zum Beispiel durch Infrastructure as Code (IaC) realisiert. IaC nutzt zur Definition einer Infrastruktur-Einheit, zum Beispiel virtuellen Maschinen, entsprechenden Code und Parameter, um die „Soll-Eigenschaften“ zu beschreiben. Immer, wenn eine Änderung an der Infrastruktur nötig ist, wird das zugehörige IaC-Skript angepasst und zur Ausführung gebracht. Diese Ausführung erzeugt dann beispielsweise neue IT-Infrastruktur. Auch ermöglicht dieses Vorgehen, deterministisch immer wieder die gleiche Infrastruktur aufzubauen, wie zum Beispiel die Testausführungsumgebung.
Der Ansatz ist mit folgenden Zielen auf das Testen übertragbar:
- Tests werden deterministisch ausgeführt – fördert Reproduzierbarkeit.
- Tests können skaliert werden – parallele Testausführung durch dedizierte Infrastruktur
- Tests können via Delivery-Pipeline automatisiert werden – fördert Flow.
- Testen integriert sich in Cloud-native Skalierungsansätze – nutzt On-demandParadigma.
Methodenentwicklung
Die Idee von Immutable Infrastructure ist es, nach erfolgreichem Aufbau der Infrastruktur keine Änderungen mehr durchführen zu können, die Infrastruktur also unveränderbar zu lassen. Somit werden einzelne Änderungen in einem skalierten Umfeld vermieden, da dies zu Inkonsistenzen führen kann. Ziel ist es, Modifikationen an der Infrastruktur nur über automatisierte Abläufe durch einen „optimierten“ Neuaufbau zu realisieren, der zum Beispiel einen Fehler eliminiert. Diese automatischen Abläufe werden mittels IaC realisiert und stellen sicher, dass alle mit einem IaC-Skript aufgebauten Infrastrukturen gleich sind. Das Skript ist somit ein Template (mit Optionen zur Parametrisierung) zur Instanziierung entsprechender Infrastrukturen.
Der Ansatz von Immutable Infrastructure ist übertragbar auf das Testen – nennen wir es Immutable Testing. Immutable Testing ermöglicht es, eine on-demand automatisierte bedarfs-spezifische Testausführung zu realisieren über deterministische, dedizierte und skalierte Infrastrukturbereitstellung. Hierfür ist die Kontrolle des Testausführungsstacks nötig.
Um zum Beispiel einen spezifischen Regressionstest deterministisch ausführen zu können, wird mittels IaC und Parametern die Testlaufzeitinfrastruktur definiert. Die Parameter sind der spezifische Anteil für den Regressionstest, da diese auch das adäquate IaC-Template wählt und so eine dedizierte Testlaufzeitumgebung bereitstellen. Diese Testlaufzeitumgebung ist das Setting für eine Testausführung. Cloud-Ressourcen ermöglichen es, die Testausführung zu parallelisieren beziehungsweise horizontal zu skalieren. Der Determinismus in der Testlaufzeitinfrastrukturbereitstellung schafft die Basis für Reproduzierbarkeit beziehungsweise On-demand-Skalierung für Regressionstests, um die Ausführungszeit durch parallele Testausführung zu reduzieren.
Durch Immutable Testing wird die Basis für einen generischen Test-Service geschaffen:
- für einen Test deterministisch ein spezifisches (Ablauf-)Set-up bereitzustellen,
- automatisiert (verschiedene) Testausführungen zu parallelisieren,
- zugehörige Testdaten werden je nach Parallelitätswunsch verteilt oder vervielfältigt.
Eine Testdurchführung beinhaltet verschiedene Elemente, die entsprechend dem Immutable Testing umgesetzt werden müssen. Ein Test beinhaltet folgende drei Kern-Elemente:
- Testobjekt (TO),
- Testfall (TF) und
- Testdaten (TD).
Im Weiteren wird auf einen Test-Runtime execution (T-Rex)-Service fokussiert. Das System und der Test beziehungsweise das TO ist nicht im Fokus der T-Rex. Somit ist das Setup des TO in einer adäquaten Laufzeitumgebung auch nicht von T-Rex adressiert. Um das Zusammenspiel von TO beziehungsweise Testumgebung und T-Rex zu realisieren, kann zum Beispiel die Continuous Integration & Continuous Delivery (CI/ CD) Chain entsprechend aufgesetzt werden.
Das TO kann mittels etablierter IaC-Ansätze im Kontext der Immutable Infrastructure bereitgestellt werden. Die TO-Umgebung kann so für den Test angefordert werden und es ist auch möglich, die Datenbasis, zum Beispiel Datenbank, entsprechend vorzubereiten. Somit kann der Anteil der TD im Kontext des TO auch über IaC bereitgestellt werden, zum Beispiel mittels Einspielen des Datenbank-Dumps.
TD haben einen Anteil im Ablaufkontext als Stimulus seitens des TF sowie als Datenbasis im TO. Die T-Rex erstellt die ganze Immutable Testinfrastruktur on demand für jeden Test. Die Konfiguration der Testinfrastruktur wird durch den Nutzer definiert. Ist die Infrastruktur erstellt, werden die Test Engines auf der Infrastruktur deployt. Die Testdaten und Testfälle werden dann zu den Engines kopiert und ausgeführt. Die Konfiguration der Infrastruktur und die hochgeladenen Testfälle und Testdaten nennen wir im Folgenden Setting. Dieses Setting ist Immutable und stets reproduzierbar. Die Bestandteile eines Settings können auch individuell unter Konfigurationsmanagement verwaltet werden (siehe Abbildung 1).
Abb. 1: Verwaltung der Bestandteile eines Settings unter Konfigurationsmanagement
In einem nächsten Schritt benötigt es eine Implementierung, die dieses logische Setting deterministisch und skalierend ausführen kann. Der T-Rex-Service muss in der Lage sein, für die verschiedenen Testtypen wie zum Beispiel Lasttests mit intensiver CPU-Nutzung passende Infrastruktur zu allokieren und für einen funktionalen Test mit mehreren Browser-Tabs eine intensive Arbeitsspeicher-Nutzung abzubilden. Dieses dynamische Allokieren und Skalieren der passenden Infrastruktur für die spezifischen Testausführungsanforderungen wird durch den T-Rex-Service realisiert. Eine mögliche Implementierung ist die Nutzung einer Container-Orchestration als skalierende Laufzeitumgebung. In diesem Fall wird eine Anforderung einer Testausführung in einen Container „übersetzt“, der die spezifische Test-Laufzeitumgebung bereitstellt und dann den TF mit den entsprechenden TD sowie gegebenenfalls vorhanden Parametern ausführt. Für eine angemessene Seiteneffekt-Freiheit und Skalierung muss die Container-Orchestration die Infrastruktur mit dem wechselnden Bedarf hoch beziehungsweise runter skalieren. Dies führt dazu, dass in sehr kurzer Zeit beispielsweise Hunderte Maschinen zur Testausführung bereitgestellt und nach erfolgter Testausführung auch wieder „entsorgt“ werden müssen. Jede Testausführung kann durch diese On-demand-Infrastrukturbereitstellung in einer dedizierten Testausführungslaufzeitumgebung automatisiert ausgeführt werden. Neben dem Bereitstellen passender Maschinen-Profile ist auch eine Balance zu finden bezüglich „Offenheit“ für die spezifische Adaption der Testablaufumgebung und anderer Aspekte wie Sicherheit. Viele Test-Frameworks und Test-Laufzeitumgebungen können bei entsprechender Offenheit zum Beispiel von einem Ökosystem aus einer 3-stelligen Anzahl von Plug-ins profitieren. Diese Möglichkeit impliziert, dass die Workload einzelner Tests maximal in einen eigenen Ausführungskontext zu isolieren ist. Diese Isolierung sollte nicht nur auf der Container-Ebene etabliert sein, sondern auch auf Maschinen-Ebene, da zum Beispiel durch das Einbinden von Plug-ins eines Drittanbieters unabsehbare Seiteneffekte entstehen können, die andere Testausführungen nicht beeinflussen dürfen. Ein möglicher Stack für ein T-Rex-Service könnte wie in Abbildung 2 dargestellt aufgebaut sein. Basierend auf einem zuverlässigen T-Rex-Service können weitere Mehrwert-Dienste und Services angeboten werden. Eine strategische Option ist es, mit T-Rex eine Service-Umgebung zu etablieren, die offen Daten aus dem Test- und Absicherungskontext bereitstellt. Wichtig hierfür ist, eine einfache Integration von T-Rex via API in die CI/CD Chain zu ermöglichen, um ein zentraler Baustein in der datengetriebenen Absicherung der Nutzer zu werden. Über die Jahre wurden, unter anderem Feedback getrieben, weitere Features und Capabilities in T-Rex integriert, wie Chaos-Engineering-Unterstützung oder Testergebnis-Visualisierung in Echtzeit während der Testausführung.
Abb. 2: Stack für einen T-Rex-Service
Praktische Realisierung
Die Volkswagen Group IT hat das Cloud-native Testing as a Service (TaaS) mittels T-Rex etabliert. Der T-Rex-Service ist aktuell als Hybrid-Cloud-Service auf beispielsweise OpenStack, AWS und Azure deployt. Dieses Deployment adressiert die Herausforderung, einen skalierenden Test-Service sowohl für public Clouds als auch für on-premise (private Cloud) am Markt zu finden. Durch die Nutzung von etablierten Open-Source-Komponenten wie JMeter, Gatling oder Selenium und Cypress ist es leichter möglich, schon vorhandenes Tester-Knowhow für Projekte zu finden. Auch ist es wichtig, den Technologie-Stack offen zu halten, um schnell und flexibel auf die Anforderungen der Automotive-IT- beziehungsweise IoTWelt reagieren zu können. T-Rex kombiniert die Ansätze der Code-Repositories und Containerisierung aus der Softwareentwicklung, um das, was an einem Test „fix“ sein soll, zu definieren. Die Aspekte, die dynamisch sein sollen, können optional über Parameter für die einzelnen Testläufe angepasst werden. Der T-Rex-Service erstellt automatisiert für jede Testausführung eine Immutable Testinfrastruktur. Auf dieser Infrastruktur wird dann ein Immutable TF mit zugehörigen Immutable TD ausgeführt. Zusammen mit dedizierter Infrastruktur ist sichergestellt, dass keine Seiteneffekte die Testergebnisse verfälschen. Da dedizierte Infrastruktur-bereitstellung gegebenenfalls kurze zeitliche Verzögerungen im Testablauf beziehungsweise Timeto-Market auslösen kann, ist es möglich, mit der Zero-Delay-Option, einer Form von „vorgezogener“ Infrastrukturbereitstellung, im Build- und Deployment-Ablauf Wartezeiten zu minimieren.
Zur Integration an Drittsysteme stehen alle Funktionen des T-Rex-Service als API bereit. So können eine Kommunikation und ein Datenaustausch mit diversen Build/ Deployment-Tools wie Jenkins sowie die Test-Planung und Lenkung erfolgen, zum Beispiel mit Jira oder Xray, um beispielsweise bedarfsgerecht Testläufe zu initiieren. Durch die resultierenden Daten aus einem Test und Schnittstellen-Offenheit ist es Nutzern möglich, sich spezifische Dashboards oder Analysen im Tool der Wahl zu gestalten. Durch die Analyse der Daten aus den Testläufen können spezifische Trends erkannt werden, wie Performanz der Builds beziehungsweise der Versionen über einen Zeitraum.
Test-Service-Anbieter haben oft das Bestreben, ihre Plattformen proprietär (vendor lock) zu gestalten. Es gibt nicht den einen Anbieter, der alles bereitstellt, und so müssten mehrere Service-Anbieter parallel genutzt werden. Tabelle 1 stellt die Feature-Einführung zeitlich dar, die erkannte Trends beziehungsweise Herausforderungen mittels des T-Rex Services adressiert.
Tabelle 1: Zeitliche Darstellung der Feature-Einführung
Zusammenfassung und Ausblick
Der vorgestellte Ansatz hat folgende Mehrwerte für T-Rex-Service-Kunden beziehungsweise -Nutzer:
- Das Pflegen (u. a. Updaten, Patchen) verschiedener Testablaufumgebungen wird minimiert.
- Das Management (u. a. sizing, auf-/abbauen) der Maschinen wird verringert.
- Eine Visualisierung der Testausführung beziehungsweise des Status wird bereitgestellt.
- Ein Report bezüglich der Testergebnisse wir bereitgestellt.
- Erstellung einer dedizierten Immutable Testinfrastruktur für jeden Testlauf.
- Reproduzierbare Testergebnisse anhand von Immutable TF und TD.
- Datensouveränität bezüglich der Ausführungsdaten und der Ergebnisse der Testläufe.
- Effizienzgewinne und Kosteneinsparungen durch zentrale Service-Weiterentwicklung.
- Schnelle Umsetzung von neuen Anforderungen durch „Inhouse-Zugriff“ beziehungsweise Inner-Source.
- Quellcode-Einsicht für Kunden vorhanden: Transparenz.
Somit bleibt mehr Zeit, sich um die Softwareentwicklung zu kümmern, statt um den Auf- und Abbau beziehungsweise die Wartung der Testlaufzeitumgebung.
Zusammenfassend ist die Immutable-Testing-Vorgehensweise eine vereinfachte reproduzierbare Art des Testens. Aktuelle und zukünftige Aktivitäten forcieren auf die offene Schnittstelle und systematische Nutzung – insbesondere auch das Teilen mit anderen Services und Plattformen – von Daten, die im Rahmen der Testausführung mittels T-Rex entstehen. Denn datengetriebene Auswertungen und Entscheidungen brauchen eine vertrauenswürdige und transparente Datenbasis – Testen ist das Schaffen von Daten bezüglich der Qualität.