Regressionstests

Automatisierte End-to-End-Tests mit Vagrant, Puppet und JBehave

Claire Fautsch

© Shutterstock / Kirill Wright

Was tun Sie, wenn Ihre BI-Lösung an seine Grenzen stößt? Dr. Claire Fautsch erklärt uns, welche Test-Lösungen sich als die wichtigsten und effizientesten heraustellten, als ihr Team bei Goodgame Studios mit der Migration von Daten und Veränderungen bei der Produktion begann.

Augrund einer schnell wachsenden Zahl neuer Spieler stieß unsere alte Business-Intelligence-Lösung, die auf PHP und MySQL basierte, an ihre Grenzen. Ein neue Data-Warehouse-Lösung musste her. Aufgrund der großen Datenmenge und des Umstands, dass die bestehende Lösung nicht nur für das Tagesgeschäft unserer Daten-Analysten, sondern beispielsweise auch für das unseres Marketing-Teams wichtig war, entschieden wir uns bei Goodgame Studios gegen eine „Big Bang“-Migration.

Stattdessen fiel unsere Wahl auf eine Schritt-für-Schritt-Migration, bei der wir auf agile Projekt-Methoden zurückgriffen. Darüber hinaus sollte dieser Ansatz eine reibungslose Überführung von Nutzern und Reporting-Tools von dem alten in das neue System ermöglichen.

Bald wurde offensichtlich, dass die (Regressions-) Tests ein wichtiger Bestandteil des Entwicklungsprozesses waren. Dafür gibt es zwei Gründe. Einerseits ist die Daten-Konsistenz zwischen dem alten und dem neuen System essentiell. Andererseits ist, da das Entwicklungs-Team mit neuen, teilweise noch unbekannten Technologien und Systemen arbeitet, eine kontinuierliche Überarbeitung notwendig. Diese umfasst die Verbesserung und Erweiterung neu entwickelter Komponenten sowie die Optimierung bereits migrierter ELT(extract load transform)-Prozesse.

In diesem Artikel werden wir erläutern, wie wir die End-to-End-Tests (E2E) automatisiert haben. Dadurch ist es uns möglich, vollständig getestete ELT-Prozesse und Regressionstests zu gewährleisten, bevor wir irgendeine Änderung auf unsere Produktion anwenden. Gleichzeitig hat dieses Vorgehen auch die oft mühsame manuelle Arbeit der Regressionstests verringert.

High Level Architecture im Überblick

Um den Aufbau unserer E2E-Tests besser zu verstehen, werden wir kurz die High Level Architecture unserer Data-Warehouse(DWH)-Lösung umreißen (siehe Abbildung 1). Die Daten in unserem DWH basieren auf Events, die von vielfältigen Quellen aus im JSON-Format an RabbitMQ-Server gesendet werden (siehe den vorherigen Artikel für weiterführende Informationen).

Claire-figure-1

Sobald die Events den Server erreichen, werden sie von einer Consumer-Komponente aufgenommen, die die Events aus den RabbitMQ-Servern ausliest, den Input validiert, anhand seiner ID separiert und auf HDFS speichert.

Von HDFS aus werden die Events via ELT-Jobs in unsere analytische Datenbank (HP Vertica) geladen. Diese Jobs sind vorausgeplant und werden von einer Komponente ausgelöst, die als Core bezeichnet wird. Zusätzlich liefern externe Datenquellen ergänzende Informationen, die für Aggregationen notwendig sind. Dazu gehören zum Beispiel Marketing- oder Download-Informationen über die Anwendung.

Anforderung

Die Anforderungen an die Tests gestalteten sich sehr einfach:

  1. Die Tests sollten automatisiert sein (sprich: Keine manuelle Intervention oder Datenvorbereitung benötigen).
  2. Es sollte möglich sein, jederzeit vollständige Regressionstests durchzuführen.
  3. Untergruppen von Tests (z. B. in Verbindung mit einem spezifischen Element) können separat ausgeführt werden.
  4. Die Tests sollten in einer in sich abgeschlossenen, produktionsähnlichen Umgebung durchgeführt werden (nicht notwendigerweise auf Ressourcen bezogen).
  5. Es sollte möglich sein, Daten aus externen Quellen zu mocken (beispielsweise aufgrund bestimmter Anfragelimitierungen von APIs).
  6. Jeder Entwickler sollte die Tests leicht auf seiner Entwicklungsmaschine ausführen können.

Nachdem die Anforderungen 4 bis 6 definiert waren, wurde schnell klar, dass die Testumgebung am besten eine Art virtueller Maschine sein sollte.

Wenn wir so nah wie möglich an der Produktionsumgebung bleiben wollen, können wir die verschiedenen Komponenten nicht nur auf einer Maschine laufen lassen. Wir brauchen separate VMs für HDFS, Vertica, RabbitQM, Consumer und Core. Darüber hinaus müssen die verschiedenen Maschinen in der Lage sein, miteinander zu kommunizieren. Nach einiger Recherche kamen wir zu dem Schluss, dass Vagrant unsere Bedürfnisse perfekt erfüllt.

Wenn es darum geht, einer virtuellen Maschine Inhalte hinzuzufügen, stellt Vagrant verschiedene Out-of-the-Box-Möglichkeiten zur Verfügung, zum Beispiel Shell-Skripts, Chef-Cookbooks oder Puppet-Module. Schlussendlich verwenden wir JBehave zusammen mit Maven als Test-Framework. Das hilft uns dabei, die Anforderungen 1, 2, 3 und 6 zu erfüllen. Im folgenden Abschnitt werden wir jedes der verwendeten Frameworks kurz beschreiben und erläutern, warum wir uns dafür entschieden haben.

Vagrant

Vagrant ist ein Open-Source-Tool, das für die Erstellung virtueller (Entwicklungs-) Umgebungen entwickelt wurde. Sein hauptsächlicher Zweck ist die Bereitstellung eines Frameworks, das den für das Aufsetzen einer Entwicklungsumgebung notwendigen Zeitaufwand verringert. Auch Ausreden wie „… aber auf meinem Computer funktioniert es“ werden durch Vagrant unmöglich gemacht.

Vagrant wird über eine Kommandozeile bedient und stellt eine Reihe von Befehlen für die grundlegende Nutzung zur Verfügung. Die Hauptbestandteile von Vagrant sind Boxen. Boxen sind vorkonfigurierte virtuelle Maschinen für Vagrant-Umgebungen. Sie sind im Grunde Templates für die Umgebung, die von Vagrant eingerichtet wird. Es gibt mehrere öffentliche Kataloge für Vagrant-Boxen, es ist aber auch möglich, eigene über das Vagrant-Box-Kommando zu erstellen.

Sobald die Box eingerichtet ist, möchte man vermutlich Software installieren oder die Konfiguration ändern. Das kann jedoch nicht manuell erfolgen. Stattdessen stellt Vagrant zu diesem Zweck sogenannte Provisioners zur Verfügung, die diesen Prozess automatisieren. Für die Bereitstelllung können entweder einfache Shell-Skripts oder Konfigurationsmanagementsysteme wie Puppet oder Chef verwendet werden. Wenn das Unternehmen ein Konfigurationsmanagementsystem verwendet, können Bereitstellungsskripte wiederverwendet werden um die Entwicklungsumgebung so produktionsähnlich wie möglich zu machen.

Wir werden regelmäßig gefragt, warum wir Vagrant und nicht Docker verwenden. Tatsächlich hätte auch Docker unsere Anforderungen erfüllt. Dass wir uns trotzdem nicht dafür entschieden haben liegt daran, dass wir so nah wie möglich an unserem Produktivzustand bleiben wollten. Das ist natürlich nur innerhalb gewisser Grenzen möglich – vor allem aufgrund limitierter Ressourcen auf den Workstations der Entwickler – aber die Verwendung virtueller Maschinen anstelle von Docker-Containern bringt uns diesem Ziel etwas näher.

Puppet

Um alles vollständig zu automatisieren und für den Endnutzer (den Entwickler) einfach zu halten, haben wir uns dafür entschieden, die Bereitstellung der Vagrant-Boxen von einem Konfigurationsmanagementsystem übernehmen zu lassen. Damit können Systemadministratoren den korrekten Status der IT-Infrastruktur definieren und automatisch aufrecht erhalten lassen.

Da wir bisher kein solches System benutzt haben, haben wir uns bei der Implementierung der Testumgebung für Puppet entschieden. Die anfängliche Lernkurve schien weniger steil als bei Chef zu sein und es erfüllte unsere Anforderungen. Puppet bringt seine eigene deklarative Sprache mit. Der Systemstatus wird über Puppet-Manifest-Dateien definiert, die im Grunde die Puppet-Programme sind. Die Manifest-Dateien enthalten eine Reihe von Ressourcen, die das System beschreiben. Manifeste können in Module aufgeteilt werden, um ihnen eine klarere Struktur zu geben und ähnliche Funktionen zusammen zu gruppieren und wiederverwendbar zu machen.

Nehmen wir zum Beispiel den Fall, dass wir einen Server mit einer MySQL-Datenbank, einer Java-Installation und installiertem Monitoring aufsetzen wollen, und einen zweiten nur mit einem MySQL-Datenbank-Setup. Dafür würden wir ein Modul für das MySQL-Setup schreiben, das wiederverwendet werden kann, um beide Server aufzusetzen.

Puppet-Installationen arbeiten normalerweise mit einem Client-Server-Prinzip, also mit einem Puppet-Master (dem Server) und Puppet-Agents an jedem Knoten, der bereitgestellt werden soll. Für unsere Vagrant-Bereitstellung haben wir uns allerdings für ein Setup ohne Server entschieden, um das Programm auf jeder Entwicklermaschine einfach und leicht verwendbar zu machen.

JBehave

JBehave ist ein Java-basiertes Framework für die Testautomatisierung, das für das Behavior Driven Development (BDD) entwickelt wurde. In diesem Framework lassen sich Tests in einer natürlichen Sprache schreiben. Jeder Testfall ist ein Szenario und verwandte Szenarien werden zu einer Story zusammengefasst.

Jedes Szenario besteht selbst aus einem Set von Steps. Es gibt verschiedene Arten von Steps und jeder Step gehört zu einem der Schlüsselwörter Given, When oder Then. Ein Given-Step definiert die Vorbedingungen eines Testfalls. Zum Beispiel kann darüber angegeben werden, wie die Input-Daten aussehen sollen. Ein When-Step definiert eine stattfindende Aktion und ein Then-Step beschreibt das Ergebnis. Es ist außerdem möglich, And zu benutzen, um mehrere Steps eines Typs zu kombinieren.

Eine Story könnte beispielsweise wie in Listing 1 aussehen.

Claire-listing-1

Diese natürliche, syntax-ähnliche Sprache ermöglicht es sogar Menschen ohne spezifisches Fachwissen, Tests zu schreiben. Irgendwann müssen wir dem System aber trotzdem noch sagen, was es mit den Steps tun soll.

Für jeden dem System bekannten Step gibt es eine annotierte Java-Methode (innerhalb eines POJO), die die Implementierung repräsentiert. Im Beispiel oben würde das (auf einem hohen Level) wie in Listing 2 aussehen.

Für geringfügig andere Zwecke verwendet, als es eigentlich gedacht ist (BDD), erwies sich JBehave auch als gutes Tool für unseren Anwendungsfall. Die meisten der ELT-Jobs, die wir E2E-testen wollen, haben zwar verschiedene Inhalte, führen aber doch häufig die gleichen Steps aus, wie beispielsweise:

  • Given: ein Event X als Input
  • When: wir Job X ausführen
  • Then: sollte die Tabelle X der Datenbank die Daten Y enthalten

Sobald ein Entwickler also ein Set von Test-Steps definiert, können auch Nicht-Entwickler neue Tests schreiben.

Claire-listing-2

Alles zusammenfügen

Mit Vagrant und Puppet haben wir eine in sich abgeschlossene Testumgebung, die den Produktivsystemen entspricht. Jeder Entwickler kann die Umgebung benutzen, nicht nur zum Testen, sondern auch zu Entwicklungszwecken.

Die JBehave-Tests sollten idealer Weise jeweils in einer neuen, sauberen Umgebung laufen. Dadurch werden negative Nebeneffekte durch zuvor gescheiterte Versuche ausgeschlossen. Darum haben wir unsere JBehave-Steps so aufgesetzt, dass sie in der Vagrant-Umgebung laufen. Vor jedem Test löschen wir die Datenbank, HDFS, und die Queues, um mit einer sauberen Umgebung anzufangen.

Da diese Umgebung ausschließlich für automatisierte Tests verwendet wird, zerstören wir damit keine Daten und machen auch nichts anderes kaputt. Jeder Testfall sollte alle Daten enthalten, die er für seine Steps benötigt.

Wir verwenden Maven, um die Tests zu starten, was durch JBehaves Maven-Plug-in erleichtert wird. Wenn wir Maven-Properties verwenden, ist es uns möglich, spezielle Testfälle durchführen. Gegenwärtig starten wir die Vagrant-Umgebung manuell, indem wir den Befehl vagrant up verwenden. Vagrant muss vollständig gestartet sein bevor Tests durchgeführt werden können. Künftig würden wir gerne das Vagrant-Maven-Plug-in einbeziehen, sodass wir auch diesen Schritt automatisieren können.

Fazit

Wir haben automatisierte Tests nicht zu Beginn des Projekts eingeführt, sondern erst einige Monate später, als schon eine Menge Code und Tests geschrieben worden waren. Daraus entstand natürlich eine große Menge an Extra-Arbeit beim ersten Schritt der Einrichtung und Konfiguration der Umgebung und dem Schreiben der Testfälle für bereits existierende Jobs.

Sobald man allerdings diese anfängliche Lücke schließt, kann die Entwicklung automatisierter Testfälle Teil des normalen Entwicklungsprozesses werden. Es kostet uns nun weitaus weniger Zeit, Regessionstests durchzuführen als zuvor (manuell). Darüber hinaus haben wir beobachtet, dass das Schreiben der automatischen Tests ein hilfreicher Schritt im Review-Prozess ist, insbesondere dann, wenn sie nicht vom Entwickler selbst geschrieben werden. Bugs können zu einem viel früheren Zeitpunkt identifiziert werden, vor allem wenn spezielle Bedingungen getestet werden.

Im Allgemeinen können wir feststellen, dass die zusätzliche Zeit, die für die Entwicklung jener Testfälle benötigt wird, kürzer ist als die Zeit, die auf Regressionstests, Reviews und Bugfixes entfällt. Zusätzlich hat die Vagrant-Umgebung den netten Nebeneffekt, dass jeder Entwickler seine eigene Entwicklungsumgebung zur Hand hat.

Die zentralen Schlussfolgerungen, die wir aus unserem Setup gezogen haben, sind:

  • Automatisierte Tests sind nicht nur eine Methode, um Regressions- und E2E-Tests zu beschleunigen, sondern helfen auch beim Review neu entwickelter Features.
  • Denken Sie nicht: „Aber die Entwicklung dieser Tests wird uns Zeit kosten“. Die Zeit, die Sie durch die Vermeidung von Bugs und die Minimierung manueller Tests gewinnen, sowie die erhöhte Qualität der Produkte, gleicht diese aus.
  • Es ist nicht gut, zu warten, bis Projekte weit fortgeschritten sind, um mit der Automatisierung der (End-to-End-)Tests zu beginnen. Fangen Sie direkt zu Beginn damit an!
  • Planen Sie das Schreiben automatisierter Tests als Standard-Bestandteil Ihres Entwicklungsprozesses mit ein, genau so wie die Dokumentation oder Unit-Tests.

Goodgame Studios ist ein deutscher Produzent von Online-Spielen, der free-to-play Spiele für das Web und Mobilgeräte entwickelt und veröffentlicht. Im Jahr 2009 gegründet, umfasst ihr Portfolio inzwischen neun Spiele mit über 200 Millionen registrierten Nutzern.

Aufmacherbild: Businessman pressing touch screen interface and select icon „software testing“ via Shutterstock.com / Urheberrecht: Kirill Wright

Verwandte Themen:

Geschrieben von
Claire Fautsch
Claire Fautsch
Dr. Claire Fautsch ist Senior Entwicklerin bei den Goodgame Studios, wo sie im Java Core Team arbeitet und am Data-Warehouse-Projekt beteiligt ist. Zuvor war sie als IT Consultant in Zürich und Hamburg tätig und war Research and Teaching Assistant an der Universität von Neuchâtel (Schweiz). Dort hat sie auch ihren Doktortitel in Informatik zum Thema des Information Retrieval gemacht, sowie ihr Bachelor- und Masterstudium der Mathematik abgeschlossen. Sie arbeitet gerne mit neuen Technologien und mag Herausforderungen.
Kommentare

Hinterlasse einen Kommentar

1 Kommentar auf "Automatisierte End-to-End-Tests mit Vagrant, Puppet und JBehave"

avatar
400
  Subscribe  
Benachrichtige mich zu:
trackback

[…] Automatisierte End-to-End-Tests mit Vagrant, Puppet und JBehave – Gegenwärtig starten wir die Vagrant-Umgebung manuell, indem wir den Befehl vagrant up verwenden. Vagrant muss vollständig … […]