×
Zahnräder Steam Punk

Testautomatisierung in komplexen Umgebungen



Wie von selbst

Automatisierte Tests als Voraussetzung für agile Softwareentwicklung sind unabdingbar. Jedes Unternehmen ist gefordert, eine passende Infrastruktur dafür zu schaffen. Die Time-to-Market ist in vielen Branchen ein entscheidender Erfolgsfaktor im Wettbewerb geworden. Immer mehr Unternehmen und Entwicklungsteams begegnen diesem Anspruch mit einem zunehmend agilen Vorgehensmodell. Um bei den damit einhergehenden kurzen Entwicklungsiterationen und Releasezyklen die geforderte Qualität dennoch halten zu können, bedarf es eines hohen Qualitätsbewusstseins, schneller Feedbackschleifen und damit effizienter Testautomatisierung. Nur wenn sich Softwareänderungen schnell, wiederholbar und umfangreich testen lassen, kann agile Softwareentwicklung ihre Stärken ausspielen.

Dann ist es auch sinnvoll, eine automatisierte Build- und Deployment-Pipeline aufzubauen. Es gibt viele Muster und Best Practices dafür, wie man mit modernen Entwicklungssprachen auf der grünen Wiese eine Umgebung schafft, die mit Test-driven Development und Tools das Leben erleichtert. Für gewachsene Strukturen, wie man sie in vielen etablierten Unternehmen vorfindet, gibt es jedoch kein Patentrezept. Hier ist jedes Unternehmen selbst gefragt. CosmosDirekt stand vor der gleichen Herausforderung und hat es mittlerweile geschafft, die Testautomatisierung zu optimieren. Auch wenn die hier umgesetzten Lösungen nicht eins zu eins adaptierbar sind, geben sie sicherlich Anstöße zur Bewältigung der eigenen Herausforderungen.

Manuell versus automatisch

Die Anwendungslandschaft von CosmosDirekt besteht aus zwei Teilen: dem Webauftritt mit dem Vertriebsportal für Versicherungen inklusive aller dazugehörigen Services und Anwendungen sowie den Backofficesystemen für die internen Prozesse. Den größten Teil des Backoffice macht die Datenbank aus, in der sich über 90 Prozent der gesamten Geschäftslogik befinden. Darüber hinaus gibt es einige Inhousewebanwendungen inklusive einer Fat-Client-Anwendung. Ende 2016 startete ein Projekt mit dem Ziel, eine Testautomatisierung für die Backofficeanwendungen von CosmosDirekt aufzubauen. Für den Webauftritt existierte eine Automatisierungslösung auf Basis von FitNesse und Selenium zum Erstellen, Verwalten und Ausführen automatisierter Tests. Im Backofficebereich wurde fast ausschließlich manuell getestet. Während es für die Webanwendung mehrere Testumgebungen gab und ein sauberes Staging möglich war, lag im Backend nur eine Testdatenbank vor, an die alle Frontend-Testumgebungen angebunden waren. Das Projekt startete in engem Austausch mit den Entwicklerteams, und so wurde schnell klar, dass die eigentlichen Probleme nicht in der fehlenden Testautomatisierung lagen. Die größten Schmerzen verursachten hier die Bereitstellung von Testdaten und die gemeinsam genutzte Testdatenbank.

Zusammenfassung individualisierte Testautomatisierung

Wenn mehrere Teams gleichzeitig auf einer Testumgebung arbeiten, kommt es unweigerlich zu Konflikten und gegenseitiger Beeinflussung. Im schlimmsten Fall blockieren die Änderungen eines Teams, zum Beispiel ein größerer Umbau an zentralen Komponenten, die Testumgebung für einen längeren Zeitraum. Das hindert andere Teams an ihrer Arbeit und bringt in manchen Fällen den Prozess zum Stillstand. Wenn in dieser Situation kurzfristige Änderungen in Produktion gebracht werden müssen, bleibt nur der Rückbau aller Änderungen. Das kann im Datenbankumfeld aufwendig und fehleranfällig sein. Bei einer kurzfristigen Produktivstellung führte dies in der Vergangenheit häufig zu zusätzlichem Stress und zu Produktivitätsverlust bei den beteiligten Teams. Um dieses Problem zu lösen, erarbeitete das Projektteam gemeinsam mit den Entwicklerteams einen Lösungsansatz, um jedem Team eine eigene Testumgebung zur Verfügung zu stellen. Er sollte die folgenden Anforderungen erfüllen:

  • Alle im Haus entwickelten Systeme sind Bestandteil jeder Testumgebung.
  • Die wichtigsten Umsysteme sollen entweder zur Verfügung stehen oder alternativ deren wichtigste Funktionen gemockt, das heißt als Platzhalter implementiert werden.
  • Der Administrationsaufwand ist aufgrund der aktuellen Personalsituation gering zu halten.

Auf das Wesentliche reduzieren

Bei CosmosDirekt ist ein Großteil der Geschäftslogik in PL/SQL innerhalb der Datenbank implementiert. Das Fundament einer Testumgebung ist daher eine eigene Instanz der Datenbank. Zunächst wurde eine Kopie der produktiven Datenbank erstellt und um alle Bewegungsdaten – alles bis auf Stamm- und Steuerungsdaten – bereinigt. Zurück bleiben der Programmcode als PL/SQL-Package und Stammdaten wie das Postleitzahlenverzeichnis und Steuerdaten. Diese nun sehr kleine Datenbank dient als Basis für die Datenbanken der Teamumgebungen, jetzt als Kopie angelegt. Für jedes Team wurde die Anwendungslandschaft (Webserver, Pods in OpenShift) neu erstellt und für die Kommunikation via Messaging wurden zusätzliche MQ-Verbindungen eingerichtet. In vielen Fällen war es aufgrund der Lizenzierungsbedingungen möglich, die außerdem benötigten Systeme mit geringem Mehraufwand bereitzustellen.

Nach der initialen Bereitstellung der zusätzlichen Umgebungen ist es wichtig, dass diese nicht dauerhaft untereinander oder vom Stand in Produktion abweichen. Ein für diesen Zweck entwickelter automatischer Abgleich stellt sicher, dass alle Änderungen in der produktiven Umgebung auch auf allen Teamumgebungen eingespielt werden (Abbildung 1). Dieser sogenannte Promotestufenabgleich setzt auf Service Management, kurz SEMA, auf. SEMA ist ein von CosmosDirekt entwickeltes Werkzeug für Change- und Releasemanagement. Es enthält Metainformationen über alle Changes, Releases, Module, deren Versionen und Umgebungen. Zudem ist das Sourcecode-Verwaltungssystem Git integriert, sodass ein Entwickler durch die Angabe der Change-ID in der Commit-Nachricht die gepushte Änderung einem Change zuordnen kann. Auch der Freigabeprozess für Änderungen ist in SEMA abgebildet. Die Ausführung von Deployments auf Umgebungen läuft über den integrierten Jenkins-Server.

Durch die Arbeit der Teams gibt es Änderungen in den Testumgebungen, die noch nicht in Produktion sind. Um die Umgebungen konsistent zu halten, muss es eine Möglichkeit geben, auf den „alten“ (produktiven) Stand zurückzukommen. Im Webumfeld stellt dies kein Problem dar, hier ist einfach das alte Artefakt (JAR- oder WAR-File) zu deployen. Im Datenbankumfeld hingegen ist dies nicht so einfach möglich. CosmosDirekt setzt dafür die Flashback-Technik von Oracle ein, die ähnlich einem Wiederherstellungspunkt in Windows die Option bietet, auf einen zuvor gesetzten Punkt zurückzuspringen.

In Erinnerung rufen

Promotestufenabgleich synchronisiert Produktivumgebung und Testumgebungen
Der Promotestufenabgleich synchronisiert die Daten der Produktivumgebung mit denen der Testumgebungen (Abb. 1).

CosmosDirekt releast wöchentlich und bringt einmal in der Woche Änderungen in Produktion. Nach der erfolgten Produktivstellung startet ein Jenkins-Job, der den Promotestufenabgleich für jede Umgebung durchführt. Dazu stellt er den letzten Flashback-Punkt wieder her. Im Anschluss wird SEMA angetriggert, um die Abweichung zur Produktion zu ermitteln und anschließend zu deployen. Dies erfolgt zuerst für Programmcode und DDL- Statements und dann für Stammdaten. Damit SEMA weiß, welche Tabellen Stammdaten enthalten, wurde eine neue Tabelle mit allen Stammdatentabellen angelegt. Ist dieser Abgleich durchgeführt, schiebt man den Flashback-Punkt auf den aktuellen Stand (Abbildung 2). DML-Statements, die auf nicht in der Tabelle aufgeführten Daten arbeiten, lassen sich im Rahmen des Promotestufenabgleichs nicht ausführen. Durch dieses Zurückspringen, Abgleichen und Neusetzen gehen alle Änderungen an der Datenbank und an den in der Zwischenzeit darin angelegten Testdaten verloren. Die Datenbank bleibt leer und damit klein und performant.

Zur Akzeptanz dieses Verfahrens unter Entwicklern wurde in SEMA eine Möglichkeit geschaffen, die Änderungen der aktuellen Entwicklung mit minimalem Aufwand erneut auf die Datenbank zu deployen. Das gelingt bei den verknüpften Modulen mit wenigen Klicks. Haben sich die Entwickler daran gewöhnt, ihre Änderungen im Sourcecode-Verwaltungstool zu pflegen, ist der Aufwand minimal: den Change, an dem das Skript mit den Änderungen verknüpft ist, auswählen, Zielumgebung bestimmen und Deployment-Auftrag auslösen. Danach triggert SEMA Jenkins an, deployt das Paket und informiert im Chat der jeweiligen Testumgebung über das Ergebnis. Nach einigen Anlaufschwierigkeiten sind die zusätzlichen Testumgebungen mit den damit verbundenen Änderungen am Entwicklungsprozess mittlerweile gut angenommen. Die ursprünglichen Probleme, wie die gegenseitige Behinderung auf einer gemeinsamen Umgebung, sind gelöst.

Jenkins-Pipeline Promotestufenabgleich mit Flashback-Technologie
Die Jenkins-Pipeline zeigt die Verwendung der Flashback-Technologie im Rahmen des Promotestufenabgleich (Abb. 2).
Listing: Kopieren der Geschäftsobjekte von anderen Datenbanken und erzeugen einer lokalen Kopie

Branchentypische Eigenheiten beachten

Neben der Verfügbarkeit zusätzlicher Umgebungen kristallisierte sich im Projektverlauf die Bereitstellung von Testdaten als zweites großes Problem der Entwickler heraus. In der Versicherungswirtschaft sind Testdaten mitunter recht komplex und teilweise schwer oder sogar gar nicht manuell zu erzeugen. Ist beispielsweise ein vor über zwanzig Jahren abgeschlossener Lebensversicherungsvertrag auszuzahlen, bietet das Fachsystem unter Umständen keine Möglichkeit, einen Vertrag in der vor zwanzig Jahren gültigen Version und zu diesen Bedingungen anzulegen. Selbst wenn es gelingt, muss dieser aufwendig über die Laufzeit fortgeschrieben werden. Erschwerend kommt hinzu, dass der Testdatensatz bei dem Test „verbraucht“ wird. Ist der Test nicht erfolgreich und zu wiederholen, ist ein neuer Vertrag erforderlich, der alle Auszahlungsvoraussetzungen erfüllt. Eine Lösung für das Testdatenproblem muss folgende Anforderungen erfüllen:

  • lokales Kopieren mit wenig Aufwand;
  • Kopieren zwischen Datenbanken mit wenig Aufwand;
  • Staging von Testdaten soll möglich sein (Produktion muss ausgeschlossen sein);
  • Testdaten sollen sicher vor Veränderung sein (Beispiel: Batchjobs)

Listing: Kopieren der Geschäftsobjekte von anderen Datenbanken und erzeugen einer lokalen Kopie

Im ersten Schritt identifizierte das Projektteam alle Geschäftsobjekte, die für Testdaten infrage kommen: Verträge, Partner und Anträge. Ein eigens entwickeltes Framework kopiert, beginnend mit der ID des zu kopierenden Objekts, alle damit verknüpften Daten in ein neues Geschäftsobjekt und gibt die neue ID zurück. Dieses „gobcopy“ getaufte Framework kann über Parameter gesteuert auch weitere damit verknüpfte Geschäftsobjekte mitkopieren. Somit lässt sich ein Vertrag mit all seinen Partnern (Vertragspartner, Begünstigter) oder ein Partner mit all seinen Verträgen und Schäden kopieren. Hierbei muss das Framework auf rekursive Verknüpfungen achten (siehe Listing). Eine Anonymisierungsfunktion ersetzt über einen Parameter alle schützenswerten Daten, für die jeweils eine passende Anonymisierung zu finden ist. Beispielsweise lassen sich Vornamen aus der Liste aller Vornamen (mit gleichem Geschlechtsmerkmal) auswählen, mit der Einschränkung, dass ein Vorname nicht identisch mit dem ursprünglichen sein darf. Bei Freitextfeldern ist ein Standardtext zu nutzen.

Mit dem entwickelten Framework kann man Testdaten von einer anderen Umgebung kopieren oder lokal klonen. Das ist jedoch unhandlich, da man immer die ID, also zum Beispiel die Vertragsnummer, auf der jeweiligen Umgebung kennen muss. Diese Daten sind auf jeder Umgebung unterschiedlich, weshalb ein Testdatenkatalog die Verwendung erleichtert (Abbildung 3). Darin sind Geschäftsobjekte mit ihrer ID auf der aktuellen Umgebung, dem Typ (Vertrag, Partner), einem Gültigkeitsbereich und einem Namen gespeichert. Mit dem Namen lässt sich das identische Testdatum auf jeder Umgebung ansprechen oder von einer Umgebung kopieren, ohne dass man die ID kennt.

Testdatenkatalog erleichtert die Verwendung auf unterschiedlichen Umgebungen
Der Testdatenkatalog schafft durch die Verwendung eines fachlichen Namens für Testdatensätze eine einfache Zugriffsmöglichkeit auf Testdaten (Abb.3).

Mit dem Promotestufenabgleich der Testumgebung ist es erforderlich, Testdaten dauerhaft zu persistieren, um sie vor dem Löschen oder Manipulieren durch Batchjobs zu schützen. Eine zusätzliche Datenbank, die sogenannte Testdatensenke, schafft Abhilfe (Abbildung 4).

Der Befehl

Codeausschnitt Katalogeintrag persistieren

persistiert den Katalogeintrag und die dazugehörigen Testdaten der Senke, und mit

Codeausschnitt Laden der Testdaten aus der Senke

werden sie aus der Senke geladen. Mit dem gobcopy-Framework, dem Testdatenkatalog und der Senke lassen sich Testdaten komfortabel erzeugen und speichern. Ungeklärt sind noch folgende Fragen:

  • Wie sind Testdaten zu organisieren, um eine effiziente Wiederverwendung zu gewährleisten?
  • Wie lassen sich Testfälle identifizieren, die ein bestimmtes Testdatum verwenden?
  • Wie ist sichergestellt, dass Testdaten mit Ablaufdatum erkannt werden (18-jähriger Kunde)?
  • Wie lassen sich Testdaten mit einem Ablaufdatum mit möglichst wenig Aufwand aktualisieren?

Alle angedachten Lösungsansätze hinsichtlich einer Wiederverwendung von Testdaten hatten den Nachteil, dass diese in Erstellung und Pflege aufwendig waren. Aus diesem Grund fiel die Entscheidung auf eine einfache und gut zu handhabende Lösung: Jeder Test bringt seinen eigenen Testdatensatz mit, der den gleichen Namen wie der Testfall bekommt.

Anfang und Ende festlegen

Schaffung einer zentralen Ablage
In der 'Senke' genannten Datenbank lassen sich Testdaten zentral ablegen, sicher verwahren und wieder laden (Abb.4).

Für Testdaten mit Ablaufdatum hat ein Testdatenkatalogeintrag ein Gültig-von- und ein Gültig-bis-Datum. Ist das Gültig-bis-Datum nicht gesetzt oder liegt es in der Zukunft, ist der Datensatz gültig. Die Felder lassen sich auch bei einem Update der Testdaten nutzen. Sind nach der Entwicklung eines Features Testdaten anzupassen, wird der neue Testdatensatz unter dem gleichen Namen gespeichert. Damit wird das „gültig bis“ des bis dahin gültigen Datensatzes auf den aktuellen Timestamp gesetzt. Somit ist nur noch der neue Testdatensatz valide.

Eine automatisierte Anpassung von Testdaten entfällt. Gegen eine solche Automatisierung spricht, dass es zu unkorrekten Konstellationen kommen kann. Beispielsweise ist es denkbar, dass ein Datensatz entsteht, in dem der Kunde einen Vertrag hält, der älter ist als der Kunde selbst. Zudem wäre dann ein Testverfahren nötig, das die automatisierte Anpassung testet.

Mit der Bereitstellung eigener Testumgebungen für jedes Team und der Möglichkeit, Testdaten mithilfe von gobcopy schnell zu generieren, sind die notwendigen Voraussetzungen für den Einsatz von Testautomatisierung geschaffen. Beim Aufbau der eingesetzten Lösung galt es, zwei wichtige Anforderungen abzudecken:

  • Auch Nichttechniker müssen die Möglichkeit haben, am Prozess der Testautomatisierung teilzuhaben.
  • Das Automatisierungsprodukt benötigt Zugriff auf mehrere Schichten der Anwendungslandschaft, um (anwendungsübergreifende) Integrationstests zu ermöglichen.

Als Testframework zur Organisation der Tests kommt FitNesse zum Einsatz (Abbildung 5). FitNesse ist ein Wiki, bei dem sich Testfälle mit einer Weboberfläche anlegen, ändern und in Suites strukturieren lassen. Die Testfälle sind nicht wie in anderen Tools als komplizierter Programmcode, sondern als einfacher Plain Text in spezieller Notation abgelegt und trotzdem über die Oberfläche direkt ausführbar. Dies ermöglicht auch Mitarbeitern mit wenig technischem Hintergrund den Einstieg in das Thema Testautomatisierung. Denn gerade deren Input ist wichtig, um eine gute Testabdeckung aus fachlicher Sicht zu erlangen. Eine weitere Hürde ist die Lesbarkeit der Testfälle. Um sie zu erhöhen, sind die beschriebenen Abläufe fachlich motiviert. Sie bestehen aus einer einfachen Abfolge fachlicher statt technischer Anweisungen (Keywords). Beispielsweise ist der Ausdruck „Log-in mit Userdaten“ einfacher zu verstehen als das technische Pendant „Fülle Feld Username, Fülle Feld Passwort, Klicke Button Log-in“. Durch diese Trennung entsteht eine Struktur, die zur Wartbarkeit der Tests beiträgt und einen nachhaltigen Einsatz ermöglicht.

Was die einzelnen Schritte (Keywords) zur Laufzeit dann wirklich ausführen sollen, ist in einer „Klebeschicht“ des Frameworks realisiert. Hier erfolgt ein Mapping der fachlichen Schritte zur passenden technischen Implementierung, die die Testtreiber für verschiedene Technologien steuert. Als Testtreiber für browserbasierte Anwendungen kommt Selenium zum Einsatz, für Fat-Client-Anwendungen AutoIt und für Datenbankzugriffe jOOQ. Dadurch ist das aufgebaute System in der Lage, die zweite Anforderung zu erfüllen und das Gesamtsystem über verschiedene Oberflächen und Services zu testen und sogar Prüfungen bis in die Datenbank hinein durchzuführen.

Visuelle Darstellung Wiki FitNesse
Das Wiki FitNesse stellt die Testautomatisierung visuell dar und ermöglicht es Fachfremden, leicht ins Thema zu finden (Abb.5).

Den Anforderungen entsprechen

Um als Testtool akzeptiert zu werden, muss es in verschiedenen Situationen einsetzbar sein. Zum einen in der lokalen Testfallentwicklung: Entwickler und Tester können schnell neue Tests schreiben, bestehende anpassen oder erweitern und sie lokal ausführen. Zum anderen in der zentralen Testausführung: Um die Durchführung von Tests in Build-Deploy-Test-Zyklen einbinden zu können, bedarf es einer zentralen Infrastruktur zur Ausführung der Tests und anschließendem Review der Testergebnisse.

Zusätzlich soll das aufgebaute Testframework Tests auf verschiedenen Umgebungen ausführen können. Dazu sind die zuvor erläuterten zentralen Testumgebungen und die lokale Entwickler- instanz abzudecken – ermöglicht durch eine Parametrisierung im Set-up. Je nach Zielsystem, eingesetzter Technologie und Umfang der getesteten Fachlichkeit kann ein einzelner Test zwei bis drei Minuten für eine Durchführung benötigen. Ab einer gewissen Anzahl an Testfällen ist daher eine parallelisierte Ausführung unabdingbar. Die bisher beschriebene Lösung zeichnet sich allerdings nicht durch eine hohe Parallelisierbarkeit aus, daher musste hier nachgebessert werden: Für die parallele Ausführung von Tests wird das auszuführende Testset beim Start ermittelt und automatisiert auf mehrere dynamisch gestartete Instanzen verteilt. Mit aktuell 20 parallel ausführbaren Tests erhält man eine Testaussage zu einem Testset mit 400 Testfällen in unter einer Stunde.

Zur Optimierung der Testlaufzeit sind die Test Sources bei jedem Push ins zentrale Testcode-Repository vorzubauen. Sie lassen sich mit einem Docker-Image zur Testausführung heranziehen. Dadurch ist eine bessere Integration in die über Jenkins gesteuerten Build-Deploy-Testzyklen möglich. Um den Anforderungen des Marktes gerecht zu werden und in möglichst kurzen Zyklen Software auszuliefern, müssen verschiedene Disziplinen zusammenspielen: Testautomatisierung, Testumgebungsmanagement, Testdatenbereitstellung und -management, außerdem die Automatisierung der Build- und Deployment-Pipelines sowie Testausführung. Am Ende bleibt die Herausforderung, Software in möglichst kurzen Zyklen ausliefern zu können. Wenn, wie gezeigt, die Disziplinen Testautomatisierung, Testumgebungsmanagement, Testdatenbereitstellung und -management gemeinsam mit der Automatisierung der Build- und Deployment-Pipelines sowie der Testausführung zusammenspielen, kann das gelingen. (nb@ix.de)

Marcel Just ist Experte für Continuous Integration und Deployment der CosmosDirekt.
Dr. Valentin Dallmeier ist Vorstandsvorsitzender der Testfabrik AG.
Björn Scherer ist Lead Test Engineer der CosmosDirekt.

veröffentlicht in iX 1/2020