Continuous Integration für Android

Artikel von Erik Nijkamp

Insbesondere im mobilen Ökosystem sind die Qualität und User Experience einer App maßgeblich entscheidend für deren Erfolg. „Continuous Integration“ (CI) als Baustein der agilen Entwicklung hilft unter anderem, eine fortlaufend hohe Qualität während der Entwicklung sicherzustellen. Jedoch ist die Realisierung dieser Methode in der Praxis nicht trivial, und Android bringt diesbezüglich besondere Herausforderungen mit sich. Wir zeigen, welchen Mehrwert „Agile“ und „CI“ liefern, welche Komponenten in CI involviert sind und wie sich die ersten Schritte hin zu einem integrierten CI-Prozess realisieren lassen.

Einleitung – Zusammenspiel Agile und Mobile

Willkommen in der Post-Wasserfall-Ära: Heutzutage ist „Agile“ die vorherrschende Methodologie in der modernen Softwareentwicklung. Nach dem „Agilen Manifest“ stehen u.a. Prinzipien wie kundengetriebene Entwicklung, hohe Produktqualität, transparente Kommunikation, iteratives Vorgehen und Adaptivität für Änderungen im Vordergrund. Zur Umsetzung dieser Prinzipien in einem Prozess kommen Vorgehensmodelle wie beispielsweise Scrum zum Einsatz.

Diese kunden- und qualitätsgetriebene Denkweise harmoniert mit den Anforderungen an die Entwicklung in mobilen Ökosystemen. Insbesondere im Mobile-Umfeld spielt Qualität (bzw. UserExperience) eine entscheidende Rolle: Gute Qualität führt zu guten Ratings im App-Store, welche das Ranking der App in Suchergebnissen positiv beeinflussen, woraufhin mehr Downloads und wiederum weitere positive Ratings folgen. Dieser Kreislauf ist durch positive User Experience getrieben, sprich Qualität beeinflusst explizit den Erfolg (oder Misserfolg) einer App.

Die Auswirkung eines „agile“ Vorgehens im Vergleich zum traditionellen Wasserfall-Modell stellt das CHAOS Manifesto 2012 der Standish Group anschaulich in Relation:

Die Studie verweist auf Projekte im Zeitraum von 2002 bis 2010. Ein Projekt gilt hier als „erfolgreich“, wenn der zeitliche Rahmen und das Budget eingehalten sowie vordefinierte Features realisiert wurden. Nach einer jährlichen Umfrage von VersionOne setzten 2012 bereits 84 % der 4.048 befragten Unternehmen agile Entwicklung ein.

Es stellt sich jedoch die Frage, wie dieses abstrakte Konzept anwendbar ist. In der Praxis beginnt die Einführung des „agile“ Gedankens oft mit zwei konkreten Maßnahmen:

1. Einführung eines agilen, iterativen Vorgehensmodells (z. B. Scrum, Kanban)

2. Unterstützung des Modells durch effiziente Automatisierung (z. B. Continuous Integration)

Das Vorgehensmodell führt einen täglichen Arbeitsablauf für die iterative Entwicklung ein. Die Automatisierung ermöglicht ein effizientes Arbeiten in diesem Modell mit kurzen Iterationen.

Continuous Integration – Eine Feedback-Schleife

Eine Möglichkeit, agile Entwicklung effizienter zu gestalten, ist die „Continuous Integration“-Methode, die zugleich einen wichtigen Eckpfeiler der agilen Entwicklung, aber auch die größte Herausforderung für agile Entwicklungsprojekte im AndroidUmfeld bildet. Im Entwicklungsalltag sind typischerweise viele Akteure (z. B. Entwickler, Designer, Copy-Writer, Tester) involviert, die parallel Änderungen an einer App vornehmen und diese unabhängig voneinander in Zweigen (engl. „Branches“) in einer gemeinsamen Versionskontrolle (z. B. Git, Mercurial) ablegen. Je länger die Akteure an diesen Zweigen unabhängig voneinander arbeiten, desto komplexer wird es später, die Änderungen in einem konsistenten Stand zusammenzuführen. Beispielsweise haben zwei Entwickler zeitgleich eine bestimmte Datei geändert, dieses Überschneiden oder der „Konflikt“ muss während der Zusammenführung (engl. „Integration“) aufgelöst werden. Vergeht viel Zeit zwischen zwei Zusammenführungen, entsteht oftmals eine umfangreiche Menge an Konflikten (auch als „Integration Hell“ geläufig), deren Auflösung in vertretbarer Zeit nicht realisierbar ist – an diesem Punkt setzt der Continuous-Integration-Gedanke an.

Ein CI-Prozess fordert das kontinuierliche Zusammenführen von möglichst kleinen Änderungen in kurzen zeitlichen Abständen. Um diesen Prozess operativ realisieren zu können, empfiehlt Martin Fowler umfassende Grundsätze [MF06]:

  • eine gemeinsame Versionskontrolle bzw. ein Code Repository
  • ein automatisierter, unabhängiger Build
  • eine automatisierte Testausführung
  • Jede Integration sollte einen Build und daraufhin die Testausführung anstoßen.
  • Der Status des letzten Builds sollte für jeden Akteur sichtbar sein.
  • Erfolgreiche Builds werden auf einer Spiegelung des Produktivsystems installiert.

Im Zusammenspiel bilden diese Komponenten einen integrierten Kreislauf, dessen Ausführung nach jeder Integration (sprich mehrere Durchläufe pro Tag) möglich ist und automatisiert den Build, Tests und die Berichterstattung anstößt:

Der Mehrwert dieses Kreislaufes ist insbesondere das zeitnahe Feedback bei dem Auftreten von Fehlern, die Transparenz über den derzeitigen Zustand der App und ein lauffähiges Artefakt des aktuellen Entwicklungsstandes. Kurz: CI bietet den Akteuren mittels Testausführung ein permanentes „Fallnetz“, welches Fehler möglichst früh abfängt, bevor sie sich zu kritischen Problematiken entwickeln. Es wird schnell deutlich, dass die Automatisierung der Testausführung für einen effizienten Arbeitsablauf unabdingbar ist und im Sinne der Qualitätskontrolle zugleich den größten Mehrwert liefert. Jedoch ist Testing an sich auf Android eine der größten Herausforderungen in dieser Umgebung und dessen Automatisierung nicht trivial.

Testing auf Android – „Wo“ „was“ „wie“ und „wann“ testen?

Im Juli 2013 waren laut der OpenSignal-Studie bereits mehr als 11.828 unterschiedliche Android-Endgeräte in allen denkbaren Formen und Größen, mit vielfältigen Bildschirmgrößen und Leistungsmerkmalen auf dem Markt vertreten. Im Vergleich hierzu wurden ledig
lich 3.997 Gerätetypen in der Studie des Vorjahres erfasst.

Es stellt sich die Frage, wie diese Vielfalt in der Praxis mit vertretbarem Testaufwand abgedeckt werden kann. Um dies zu beantworten, definieren wir im Folgenden schrittweise ein Konzept für das Testing:

Das „Wo“ – Um sich den zeitlichen Aufwand eines sukzessiven Tests auf den obigen 32 prägnanten Android-Versionen und Bildschirmkombinationen zu ersparen, empfehlen wir eine Einschränkung auf die Top 5 bis 10 der vertretenen Endgeräte. Hierbei sollte berücksichtigt werden, dass diese Auswahl an Referenzgeräten möglichst eine breite Diversität im Hinblick auf Android-Versionen und Bildschirmmerkmale repräsentiert. Beispielsweise lassen sich die populären Geräte anhand der OpenSignal-Studie 2013 [OS13] oder der Infographics von Handset Detection [IHD] ermitteln.

Das „Was“ – Mobile Apps müssen auf den Displays von vielfältigen Smartphones und Tablets mit verschiedenen Größen und Auflösungen bedienfreundlich (Stichwort „responsive design“) und korrekt dargestellt werden. Zugleich sollten Apps funktional mit möglichst vielen Gerätespezifikationen (Arbeitsspeicher, CPU, Sensoren usw.) kompatibel sein. Es ist daher sinnvoll, vorab eine Liste an spezifischen Testplänen zu definieren, die möglichst umfassend die Funktionalität der vorliegenden App abdeckt, und vielfältige Faktoren (wie beispielsweise die Änderung der Netzwerkgeschwindigkeit, eingehende Anrufe oder Sperrung des Bildschirms) berücksichtigen.

Das „Wie“ – Die zuvor definierten Testpläne sollten im Folgeschritt durch verschiedene Testmethodiken realisiert werden. Im Kontext des automatisierten Testings für Continuous Integration empfehlen wir drei Bausteine:

1. Unit- und Integrationstests isolierter Module zum effizienten Isolieren von Regressionen

2. Stress-Tests durch zufällige Eingaben auf der Bedienoberfläche zum explorativen Aufdecken von unerwarteten Fehlern

3. UI-Tests für Features und komplexere Szenarien mit mehreren Referenzgeräten

Zur Umsetzung dieses kontinuierlichen Regression-Testings auf möglichst unterschiedlichen Referenzgeräten (bzw. Gerätekonfigurationen) bietet der Android Emulator eine pragmatische Lösung. Die charakteristischen Merkmale eines Android-Gerätes lassen sich
mit diesem Werkzeug auf einem herkömmlichen PC mithilfe eines virtualisierten Endgerätes nachstellen.

Das „Wann“ – In einem Continuous-Integration-Kreislauf sollte die automatisierte Testausführung stets nach einem erfolgreichen Build zumindest auf dem Android Emulator ausgeführt werden. Dennoch ist die Qualitätssicherung auf realen Geräten unabdingbar. In der Praxis unterscheiden sich die virtualisierten Referenzgeräte oftmals in kleineren (jedoch für manche Apps signifikanten) Aspekten: keine herstellerspezifischen Änderungen am Android-Betriebssystem oder keine Unterstützung für Headphones und Bluetooth. Aus diesen Gründen empfehlen wir ein zweistufiges Vorgehen:

1. Automatisiertes Regression-Testing per virtuellem Endgerät: kontinuierliches Regression-Testing auf Referenzgeräten zur frühzeitigen Identifizierung von essenziellen Fehlern.

2. Manuelles Pre-Release-Testing per realem Endgerät: intensives (vorwiegend manuelles) Testen auf realen Endgeräten vor der Veröffentlichung im Google Play Store in einem „staged rollout“ z. B. Alpha- und BetaTests [siehe GP].

Howto – Umsetzung in der Praxis

In der Praxis bedeutet die technische Einführung eines Continuous-Integration-Prozesses in der Regel das Verknüpfen einiger Werkzeuge zu einer „toolchain“. Für die spezifische Umsetzung im Android-Ökosystem empfehlen wir eine Versionskontrolle (Git), ein Build-Tool (Apache Maven 3), einen BuildServer (Jenkins), Testing-Frameworks (JUnit 3, Robotium, TestObject) und Referenzgeräte (Avndroid Emulator). Die Einrichtung der Werkzeuge an sich ist umfangreich dokumentiert, wir konzentrieren uns auf (1) das Definieren eines Referenzgerätes, (2) das Anlegen von Tests und (3) das automatisierte Ausführen des Tests auf dem Referenzgerät.

(1) Anlegen des emulierten Referenzgerätes

„Samsung Galaxy S III“ Wir richten nun einen Emulator ein, der in seiner Konfiguration dem Endgerät „Samsung Galaxy S III“ entspricht. Hierfür verwenden wir die Open-Source-IDE Eclipse mit dem Android Developer Tools (ADT) Plugin, das im Android SDK enthalten ist.

1. Gerätedefinition anlegen – Im Android Virtual Device Manager lassen sich die virtuellen Geräte verwalten. Die Spezifikationen der Geräte finden sich unter „Device Definitions“. Eine Auswahl an Vorlagen ist bereits vorhanden. Falls das Wunschgerät nicht dabei ist, kann eine eigene Definition erstellt werden. Ein Gerät mit einem der größten Marktanteile ist beispielsweise das Samsung Galaxy S III. Dieses hat folgende Eigenschaften: Screen Size: 4.8 inch, Resolution: 720 × 1280 Pixel, RAM: 1.024 MB (siehe Abbildung 4).

2. Virtuelles Gerät erstellen – Die Gerätedefinition kann nun für das Erstellen eines virtuellen Geräts verwendet werden. Folgende Eigenschaften sind dabei zu ergänzen: Das Galaxy S III wird standardmäßig mit Android 4.0.4 ausgeliefert. Wir wählen daher als ähnlichste SDK-Version Android 4.0.3 im Feld „Target“ des Konfigurationsassistenten. Die Größe des VM Heap setzen wir auf 256 MB. Zusätzlich aktivieren wir für die hardwarebasierte OpenGL-Beschleunigung die Option „Use Host GPU“ (siehe Abbildung 5).

3. Starten des virtuellen Gerätes – Nun lässt sich das neu angelegte virtuelle Gerät „meinS3” über den AVD Manager starten (siehe Abbildung 6).

(2) Anlegen von Unit- und UI-Tests

Wie bereits erwähnt, empfehlen wir eine Kombination aus Unit-Tests im White-BoxAnsatz, explorative Stress-Tests und UI-Tests als Black-Box-Tests.

Unit-Tests können wie gewohnt mit JUnit geschrieben werden, wobei das Android SDK allerdings nur Version 3 unterstützt. Das Android Testing Framework bietet verschiedene von InstrumentationTestCase und AndroidTestCase abgeleitete Basisklassen (ProviderTestCase2, ApplicationTestCase, ActivityInstrumentationTestCase usw.), die für verschiedene Szenarien (z. B. das Testing einer Activity) spezialisiert sind (siehe [ADT]).

Beispielhaft zeigt Abbildung 7 das isolierte Auslösen eines Intents mit einem zusätzlichen Parameter userId. Daraufhin wird geprüft, ob der Parameter entsprechend an ein TextView-Element weitergereicht wurde. Stress-Tests können durch pseudozufällige Touch- und Keyboard-Eingaben auf der Bedienoberfläche einer App explorativ Fehler zum Vorschein bringen, die in vordefinierten Testplänen nicht berücksichtigt wurden.

Hierdurch können ohne viel Aufwand hunderte Nutzer simuliert werden. Der Test bricht als nicht erfolgreich ab, sobald die Anwendung eine Exception wirft. Das „MonkeyRunner“Tool ist in der Android SDK zu finden, alternativ (siehe Abbildung 8) bietet das TestObjectSkript die randomInput()-Funktion. Neben Unit- und Stress-Tests können UI-Tests als Block die Funktion einer App aus Kundensicht validieren. UI-Tests lassen sich neben TestObject mit Frameworks wie Robotium, Appium oder Sikuli umsetzen.

(3) Integration von Build, Tests und Referenzgerät in Jenkins

Wir empfehlen Apache Maven3 als Werkzeug für einen automatisierten Build der Android-App. Eine Anleitung zur Verwendung des „android-maven-plugin“-Plugins für Maven3 stellt Lars Vogel bereit (siehe [VOG]). Daraufhin können Sie Ihr Maven-basiertes Projekt wie gewohnt im Jenkins-Build-Server anlegen.

Neben dem eigentlichen Build führt der Jenkins-Server (oder genauer: Maven3) Ihre JUnit Tests aus. Diese Tests benötigen eine Androidbasierte Ausführungsumgebung, sprich Ihre App wird in einem Android-Betriebssystem (z. B. im Android Emulator) ausgeführt. Die Ausnahme bildet hier das Robolectric Framework [ROB], welches in vielen Fällen die Testausführung ohne Android ermöglicht.

Diese Verknüpfung zwischen Jenkins-Build und Android Emulator bietet das JenkinsPlugin „Android Emulator Plugin“. Eine Anleitung zur Konfiguration des Plugins bietet Vítor Baptista (siehe [VIT]). Für die Ausführung von TestObject-UI-Tests mit Jenkins wird ein Ant-Task definiert (siehe [TO]), der Maven3Plugin „maven-antrun-plugin“ (siehe [ANT]), nach dem der Build ausgeführt wird.

Fazit – Continuous Integration als „Fallnetz“

Richtig eingesetzt ist Continuous Integration insbesondere im fragmentierten AndroidÖkosystem ein mächtiges Werkzeug zur kontinuierlichen Qualitätskontrolle und bietet ein „Fallnetz“ im Sinne von frühzeitigem Feedback bei Fehlern, automatisiert gesicherter Qualität, Transparenz im Projektfortschritt, Vermeidung von Codekonflikten, ständig vorzeigbarem Entwicklungsstand, kurzer Timeto-Market. Natürlich ist die Umsetzung dieses Prozesses mit einer nicht vernachlässigbaren Initialinvestition verbunden, jedoch sind die Vorteile unmittelbar spürbar. Kurz: Continuous Integration erleichtert der Entwicklung den Alltag, rentiert sich wirtschaftlich innerhalb kurzer Zeit und führt durch automatisiertes Testing in der Regel zu einem zufriedenen Kunden. Nutzen Sie Continuous Integration in Android-Projekten?

Referenzen

[MF06] http://www.martinfowler.com/articles/continuousIntegration.html
[CM11] http://www.immagic.com/eLibrary/ARCHIVES/GENERAL/GENREF/ChaosManifest_2011.pdf
[OS13] http://opensignal.com/reports/fragmentation-2013/
[GP] https://support.google.com/googleplay/android-developer/answer/3131213?hl=en
[IHD] http://www.handsetdetection.com/blog/where-in-the-world-are-android-devices-showing-up-infographic/
[VIT] http://vitorbaptista.com/continuousintegration-for-android-apps-withjenkins-and-maven3/
[VOG] http://www.vogella.com/articles/AndroidBuildMaven/article.html
[ROB] http://robolectric.org
[TO] https://help.testobject.com/docs/guides/ant-task/
[ADT] http://developer.android.com/tools/ testing/testing_android.html
[ANT] https://help.testobject.com/docs/guides/ant-task/

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Kategorien

Recent Posts