Cortex Media in Melbourne: Auswirkungen des Online-Status bei WhatsApp&Co

In der letzten Woche gingen Meldungen über einen italienischen Studenten durch die Presse, der mit Hilfe von Screenshots aus WhatsApp und OCR Erkennung in der Lage war, automatisiert den Online-Status von einzelnen WhatsApp-Nutzern zu protokollieren. Eine Forschergruppe aus Ulm, welche sich schon seit Herbst 2013 mit diesem Thema beschäftigt, hat nun ein Paper veröffentlicht, welches ein noch viel bedrohlicheres Gefahrenszenario aufzeigt und dessen Inhalt die bisherigen Erkenntnisse deutlich übersteigt:

Andreas Buchenscheit und seine Kollegen haben gezeigt, dass der Online-Status für eine beliebige Rufnummer direkt am WhatsApp Server abgegriffen werden kann. Dieser Zugriff ist selbst dann möglich, wenn das sogenannte „zuletzt online“ Feature durch den Benutzer explizit deaktiviert wurde. Die Gruppe entwickelte ein Tool, mit dem es möglich ist, auf diese Weise eine beliebige Anzahl von Personen ohne deren Wissen oder Zustimmung gleichzeitig zu überwachen und lückenlos zu protokollieren, zu welchen Zeiten und wie lange WhatsApp aktiv genutzt wird. Ziel der Forschungsarbeit war jedoch nicht nur, die generelle Möglichkeit dieses Abhörens zu demonstrieren, sondern auch zu zeigen, was für Auswirkungen dies auf die Privatsphäre eines Nutzers haben kann. Diese Auswirkungen sind deutlich weitreichender als bislang angenommen.

Durch eine Studie, bei der der WhatsApp Online-Status von zwei unabhängigen Gruppen mit jeweils 10 Probanden über einen Zeitraum von vier Wochen aufgezeichnet und analysiert wurde, konnten die Ulmer Forscher zeigen, dass sich anhand des Online-Status komplette Nutzungsprofile erstellen lassen. Es wurden Metriken entwickelt, um aus den Daten weitreichende Informationen über den Tagesablauf und die Gewohnheiten einer Person abzuleiten. Zum Beispiel kann festgestellt werden, wann eine Person morgens aufsteht und abends ins Bett geht. Es kann lückenlos protokolliert werden, ob WhatsApp zu unangebrachten oder sogar verbotenen Zeiten genutzt wird (z.B. während der Arbeits- oder Schulzeit). Arbeitgeber könnten Mitarbeiter überwachen und überprüfen, wie lange ihre Mitarbeiter nachts wach sind und somit ob sie fit genug zur Arbeit kommen oder bis 4:30 Uhr eine Party gefeiert haben. Bei einer Testgruppe konnte sogar die Teilnahme der kompletten Gruppe an einer Studentenparty anhand der aufgezeichneten Daten nachgewiesen werden.

Besonders kritisch ist die im Paper vorgestellte Möglichkeit, aus den generierten Nutzungsprofilen ein Kommunikationsmuster abzuleiten. Den Forschern gelang es, mehrere Konversationen zwischen den Probanden zu identifizieren. Diese Technik lässt sich sowohl im privaten Umfeld („Chattet meine Frau mit Person X?“) oder geschäftlichen Umfeld („Ist Mitarbeiter X mit Person Y in Kontakt“) einsetzen, aber auch von Staaten und Regierungen, welche die Kommunikationsaktivitäten und -partner ihrer Bürger aus politischen Gründen überwachen wollen.

Dies zeigt ein grundsätzliches Problem auf, dass sich keineswegs nur auf WhatsApp beschränkt, sondern im Prinzip alle modernen Messenger und Kommunikationssysteme betrifft. Vermeintlich harmlose Metadaten, die sorglos geteilt oder erfasst werden, verraten oft viel mehr über Nutzer, als diese sich vorstellen können. Darum sollten Mechanismen zum Schutz dieser Daten ein grundlegender Bestandteil jedes Systemdesigns sein.

Das am Dienstag veröffentlichte Paper wird im November auf einer internationalen Konferenz im australischen Melbourne vorgestellt (MUM 2014: The International Conference on Mobile and Ubiquitous Multimedia). Die Forschergruppe, die aus Experten für Internet Security der Firma Cortex Media GmbH aus Ulm, sowie aus Wissenschaftlern der Universität Ulm und der Carnegie Mellon University, Pittsburgh, USA besteht, hofft durch die Veröffentlichung der Ergebnisse auf potentielle Auswirkungen der Nutzung von mobilen Chat-Apps aufmerksam machen zu können.

Das Paper zur Studie ist zu finden unter: www.uulm.de/?WhatsAppPrivacy

 

Was sagt die Presse?

In Unsere Projekte veröffentlicht

jQuery Listener und AJAX

Ein klassisches Problem, wenn man HTML Elemente dynamisch durch die HTML-Response eines AJAX Calls ersetzt, ist, dass danach die auf diesen Elementen gebundenen Listener nicht mehr gebunden sind. Das liegt natürlich in der Natur der Sache und führt sehr häufig zu komplexem und schwer zu wartbaren Code, der die Listener im Success-Callback neu bindet.

Für dieses Problem gibt es eine einfache und elegante Lösung, die aber recht oft übersehen wird. Mit einem dritten Argument in der .on()-Function und dem initialen Selektor auf ‚document‘, wird der Listener auch weiterhin gebunden sein, wenn der entsprechende HTML darunter durch ein .html(‚…‘) ersetzt wird.

$(document).on('click', '.your-css-selector', function(event){
 // ... your code here.
}); 

Sehr einfach und auch sehr praktisch.

In Unsere Projekte veröffentlicht

Ulm in 30 Sekunden

Für besondere Internetseiten und Portallösungen experimentieren wir immer wieder mit den unterschiedlichsten und manchmal aus sehr außergewöhnlichen Dingen. Dieses Mal mit einer vorprogrammierten Drohne, die handgesteuert „Ulm in 30 Sekunden“ zeigen soll. Die Geschwindigkeit des Videos wurde nicht bearbeitet, wir haben lediglich einige Farben angepasst. Als Kamera kam eine GoPro Hero 3 zum Einsatz, welche mit einem elektronischen Bildstabilisator und einer kardanischen Aufhängung direkt an der Drohne befestigt wurde. Das Video hat sich innerhalb kurzester Zeit über die Solzialen Netzwerke verbreitet und nach 2 Tagen bereits über 10.000 Views. Aber seht selbst:

In Unsere Projekte veröffentlicht

Cortex Media Push-Server und die Frag-Mutti Apps

1402518737_bubbles

Für unsere Smartphone-Apps für Android und iOS benötigten wir schon relativ früh einen eigenen Push-Server, um diverse Nachrichten und Statusmessages direkt in die Apps pushen zu können. Die damals vorhandenen kommerziellen und Open-Source Angebote haben uns nicht überzeugt, so dass wir einen eigenen Push-Server entwickelt haben.

Der Push-Server

Wir haben uns für ein Konzept entschieden, das beide für uns wichtigen Push-Dienste vereint: Den Apple Push Notification Service (APNs) sowie sowohl das Google Cloud to Device Messaging Framework (C2DM) und als auch später den Google Cloud Messaging (GCM) Dienst. Der Push-Server empfängt über die entsprechenden APIs unserer Apps die Nachrichten und pusht, je nach Quelle, die Nachrichten weiter an die von Google und Apple angebotenen Schnittstellen. Hierbei kümmert sich der Server um ein logisches Buffering der Push-Messages, um beispielsweise bei einem Newsletter die Schnittstellen übermäßig zu beanspruchen. Um auch später für andere Pushes-Services bereit zu sein (vielleicht doch nochmal Windows Phone?), lässt sich der Server auch um Plugins erweitern, die andere Schnittstellen ansprechen können.

Anbindung der APIs

Für die Kommunikation mit dem Push-Server haben wir uns bewusst für eine direkte Socketverbindung zum Server entschieden. Dadurch erreichen wir vor allem bei sehr hohem Traffic oder der massenhaften Versendung von Pushes (kein Spam, dazu unten mehr) einen höheren Durchsatz als über eine HTTP-API oder ähnliches. Und da unsere APIs in der Regeln direkt mit dem Server sprechen können, und der Server auch nicht von externen Usern oder Diensten angesprochen wird, war dies die beste Alternative.

Der Server selbst verarbeitet die ankommenden Pushes über einen internen Buffer, der diesen nach und nach und je nach Schnittstelle abarbeitet. So kann die Anlieferung der Pushes von den APIs ohne Verzögerung geschehen und der Server kümmert sich asynchron um den Versand. Die Erfahrung zeigt, dass die Pushes oft schneller am Server ankommen wie die Service von Apple und Google diese entgegen nehmen, auch wenn diese Schnittstellen in der Regel sehr schnell reagieren. Falls ein Push nicht zugestellt werden kann, kann dieser auch wieder, je nach Response vom APNs oder GCM, zurück in den Buffer gelegt werden.

Die Frag-Mutti App

Die iOS und Android App für Frag-Mutti, die wir 2012 für Frag-Mutti.de entwickelt haben, nutzt unseren Push-Server zur Versendung diverser Nachrichten direkt an die Apps. Darunter z.B. praktische Tipps zu Pfingsten oder den Tipp der Woche.

Die Apps sind aktuell auf knapp 200.000 Geräten installiert (Stand Mitte 2014) und in etwa die Hälfte aller User haben die Push-Nachrichten aktiviert. Wenn Frag-Mutti Pushes verschickt, geschieht dies in der Regel gesammelt innerhalb eines kurzen Zeitfensters. Genau hier hat der oben beschrieben Buffer seine Stärken. Wenn Frag-Mutti mehrere 10.000 Pushes auf einmal anliefert, legt der Push-Server diese in den internen Buffer und arbeitet einen nach dem anderen ab. So kann der Client seine Daten in kurzer Zeit versenden, ohne weiter auf den Server und die dahinter liegenden Apple- und Google APIs zu achten.

In Technischer Hintergrund, Unsere Projekte veröffentlicht

Mehrsprachigkeit mit PHP und Smarty

In vielen unserer Projekte ist für den Kunden Mehrsprachigkeit ein sehr wichtiges Thema. Sei es bei einer App, die in verschiedensprachigen AppStores veröffentlicht werden soll, bei einer Intranet Anwendung, die für Zweigstellen in verschiedenen Ländern bereitgestellt wird oder bei der neuen Firmenhomepage, die in mehreren Sprachen abrufbar sein soll.

Während manche unserer eingesetzten Frameworks & CMS-Systeme von Haus aus eine sehr gute Unterstützung von Mehrsprachigkeit mitbringen (z.B. die App-Entwicklungsumgebungen von Android und iOS), gilt es bei nativen PHP Projekten selbst für eine sinnvolle Integration verschiedener Sprachen zu Sorgen.
 Diese Integration von Mehrsprachigkeit in einem Software-Projekt erfordert eine gute Planung und konsequente Umsetzung des Features von Beginn an. Nur so lässt sich vermeiden, dass man am Ende der Entwicklung eine Codebasis steht, bei der die Mehrsprachigkeit unwartbar und tief im Code versteckt ist.
Am Beispiel unseres oft eingesetzten hauseigenen PHP Frameworks möchte ich vorstellen, wie wir die Mehrsprachigkeit sinnvoll in das bestehende System integriert haben.

Mehrsprachigkeit mit PHP

Der naive Implementierungsansatz ist es, die verschiedenen Sprachen per switch oder if-else Konstrukte direkt bei der jeweiligen Codestelle abzufragen und entsprechend auszugeben:

 if($config::getCurrentLanguage() == Config::ENGLISH) {
     echo "Hello";
 } if($config::getCurrentLanguage() == Config::SPANISH) 
     echo "Ola";
 } else {
     echo "Hallo";
 }

Dieser Ansatz hat aber einige gravierende Nachteile:

  • Keine zentrale Stelle zum Editieren des Texts
  • Hinzufügen einer neuen Sprache erfordert, dass alle Stellen im Code gefunden und angefasst werden
  • Die Programmlogik wird durch eine große Menge von „unnötigen“ if-else Konstrukten aufgebläht und der Code wird immer schwerer wartbar

Das grundlegende Ziel der Implementierung der Mehrsprachigkeit sollte deshalb eine gekonnte Trennung der Codebasis und der tatsächlichen Übersetzung sein. PHP bringt mit der Unterstützung von GNU gettext schon eine relativ elegante Möglichkeit mit, diese Trennung vorzunehmen.

Um gettext zu benutzen, werden alle Textausgaben innerhalb des Quellcodes in einen gettext Aufruf gekapselt:

    echo "Das ist ein toller Tag!";

wird zu

    echo gettext("Das ist ein toller Tag!");

    /*Alternativ kann auch _ statt gettext verwendet werden*/
    echo _("Das ist ein toller Tag!");

Beim Aufruf schaut gettext nun in den bereitgestellten Übersetzungsdateien nach, ob eine entsprechende Übersetzung vorhanden ist. Falls ja, wird der übersetzte Text ausgegeben, ansonsten der Original-String.

Die Übersetzungsdateien sind sogenannte .po (Portable Object) Dateien, welche folgendermaßen aufgebaut sind:

 msgid "Das ist ein toller Tag!"
 msgstr "It's a wonderful day!"

msgid ist dabei der eindeutige Identifikator des zu übersetzenden Strings (Es werden statt der sprechenden Standardübersetzung auch gern abstrakte Identifier wie WELCOME_TO_PAGE_GOOD_DAY verwendet. Der Programmierer muss selbst entscheiden, ob es sinnvoller ist, dass diese abstrakten Identifier oder der Text in der Ursprungssprache zurück gegeben werden, sollte keine Übersetzung gefunden werden.).

Hinter dem Schlüsselwort msgstr folgt nun die jeweilige Übersetzung für die unterstützten Sprachen. Es muss für jede unterstützte Sprache ein eigene solche .po angelegt werden. Diese Dateien werden danach mit dem Konsolenkommando msgfmt in binäre .mo (Machine Objekt) Dateien umgewandelt.

PHP entscheidet dann auf Basis der gesetzten Locale welche Übersetzung gebraucht wird und versucht das passende .mo zu finden und den entsprechenden String auszulesen.

Unterstützung der Template Engine Smarty

Unser selbstentwickeltes PHP-Framework setzt auf eine konsequente Trennung von Darstellung und Logik. Wir setzen deshalb die Template Engine Smarty ein, um diese Trennung zu erreichen. Smarty benutzt dabei zur Darstellung Template Dateien (HTML angereichert um Steuerungsanweisungen), welche durch eine korrespondiere PHP Datei befüllt werden.

 
 /*Beispiel eines Smarty Templates, Steuerungsanweisungen werden durch { } geskapselt*/
 <table>
 {foreach $persons as $person}
     <tr>
         <td>{$person->getName()}</td>
     <tr>
 </table>

Da der Text nun in diesem Template File und nicht mehr in PHP selbst stehen, müssen wir auf ein Smarty Plugin zurückgreifen, welches die gettext-Abfragen übernimmt. Nach intensiver Suchen und ausführlichen Tests hat sich das Plugin smarty3-i18n als beste Alternative heraus kristallisiert.

Eine einfache gettext-Anweisung sieht mit diesem Plugin in Smarty wie folgt aus:

 {t}Bitte geben Sie Ihren Namen ein:{/t}

Übergabe von Variablen

Oft ist der Text bei Anwendungen nicht komplett statisch, sondern abhängig vom aktuellen Zustand der Anwendung. Ein einfaches Beispiel dafür ist eine Willkommensnachricht für den User nach dem Login, bei dem der Benutzer mit seinem Namen begrüßt wird. Es ist dabei nötig, den Text in den Übersetzungsdateien mit dem Namen des Users aus der Datenbank anzureichern.

Dazu ist es möglich, Ersetzungsvariablen innerhalb der Übersetzungen zu definieren. Im unseren einfachen Beispiel würde das so aussehen:

  /*Die po-Datei*/
 msgid "Willkommen auf der Webseite, %1 %2"
 msgstr "Welcome to the website, %1 %2"
   /*Smarty Template, die Variable $user wird aus PHP gefüllt*/
   {t firstname=$user->getFirstname() lastname=$user->getLastname()}Willkommen auf der Webseite, %1 %2{/t} 

Bei der Ausgabe des Smarty Templates wird dann automatisch der Text um den Namen des Benutzers ergänzt und ausgegeben. Die Bezeichnung der Variablen ist dabei egal, es zählt bei der späteren Ersetzung nur die Reihenfolge mit der die Variablen übergeben werden.

In Unsere Projekte veröffentlicht

Testphase Bilderupload unter http://upic.cc

Das mit den Bilderuploads im Web ist so eine Sache. Im Zeitalter von Social Media und dem damit verbundenen Drang zur Selbstdarstellung, ist man es gewohnt, in einfacher Form Bilder ins Web hochzuladen. Problematiken zum Datenschutz oder auch Urheberrechtsverletzungen erscheinen eher zweitrangig. Der Upload muss einfach nur gut funktionieren und meine Freunde bzw. die Internetgemeinde muss (von mir) begeistert sein. Was aber machen die meisten Anbieter von Bilderuploads? Entweder werden die Bilder sehr komprimiert und verkleinert (siehe Dienste wie Facebook, WhatsApp, usw.) oder die Darstellung der Bilder ist mit Werbung und anderen störenden Elementen übersäht (siehe quasi alle anderen Image-Hostern). Was aber, wenn ich z.B. als Fotograf einfach mal kurz ein Bild meinem Kunden zeigen möchte, ohne Werbung, ohne große Datenschutzbedenken, vielmehr in Top Qualität?

Da wir des Öfteren derartige Anfragen bekommen haben, wurde beschlossen, ein kleines Projekt namens http://upic.cc zu starten und einfach mal auf unseren freien Serverkapazitäten anzubieten — selbstverständlich kostenlos! Folgende Funtionalitäten leistet upic.cc:

  • Ein Benutzer zieht einfach ein JPG-Bild mit der Maus ins Browserfenster oder wählt auf seinem Handy ein Bild aus
  • Nun findet eine clientseitige Komprimierung und evtl. Verkleinerung (wobei wir hier von einer „Verkleinerung“ auf 3200 Pixel sprechen) statt, um wichtige Bandbreite zu sparen
  • Der Uploadprozess wird mit einer Progress-Bar angezeigt. Hierfür liefert der Server immer eine präzise Rückmeldung zu den übertragenen Daten
  • Das fertige Bild wird im Vollbild dargestellt und kann nun über einen privaten Link geteilt werden
  • Nach 10 Tagen löschen wir das Bild, so bleibt es auch nicht ewig im Netz weil man es vielleicht längst vergessen hat. Außerdem möchte man ja eh nur „kurz das Bild zeigen“
  • Eine Übersicht über hochgeladene Bilder findet sich dann weiterhin auf der Startseite
  • Das ganze auf einer „coolen Domain“

Klingt das gut? Einfach mal ausprobieren: http://upic.cc

In Unsere Projekte veröffentlicht

Cortex Tickets – Online-Ticketvorverkauf für jedermann

Als die Cortex Media GmbH startete, war ein großes Ziel von uns, neben dem Anbieten von individuellen Lösungen im Bereich Webentwicklung, Server und Internetapplikationen auch der Vertrieb von eigenen Produkten. Wir begannen deshalb relativ zügig mit einigen Brainstormings, was denn dieses erste eigene Produkte sein könnte. Auf Grund von persönlichen Erfahrungen beim Veranstalten von kleinen und größeren Theater-, Musical- und Konzertveranstaltung war die Idee schnell geboren:

Wir wollten einen kostengünstigen Online-Ticketverkauf als Alternative für kleine und mittelgroße Veranstalter schaffen, denen die Konditionen und Preise großer Anbieter nicht zusagen und die von der oftmals unnötigen Komplexität abgeschreckt sind. Auch ein Name war schnell geboren und so starteten wir im Februar 2010 mit der Entwicklung unseres eigenen Online-Ticketverkauf-Portals cortex-tickets.de.

Die Startseite unseres Portals www.cortex-tickets.de

Die Startseite unseres Portals www.cortex-tickets.de

Oberste Maxime bei der Entwicklung waren natürlich die Grundsätze, die wir auch bei all unseren Kundenprojekten anlegen: Hohe Sicherheit, ausgezeichnete Performance, Hochverfügbarkeit, Mobilität, Innovation und die Einbeziehung neuster Technologien. Außerdem war uns ein besonderes Anliegen, eine faire Preisstruktur und eine sehr einfache und intuitive Bedienoberfläche zu schaffen.

Neben der Webentwicklung des Portals bot das Projekt noch weitere spannende Herausforderungen, die viele der Themenbereiche abdecken, in denen die Cortex Media GmbH auch als Dienstleister Auftritt:

  • Der Benutzer druckt die gekauften Tickets in unserem System selbst bei sich zu Hause aus. Dafür wurde ein Modul entwickelt, das eine beliebige Anzahl von Tickets mit Bar- und QR-Codes als PDF generiert und diese dem Benutzer per E-Mail schickt bzw. zum Download bereitstellt.
  • Die Webplattform speichert Kundendaten und Bezahlinformationen. Diese Daten sind natürlich sensibler als die meisten Daten, die gewöhnliche Webseiten speichern. Wir haben deshalb hier besonderen Augenmerk auf Datenschutz und Datensicherheit gelegt.
  • Natürlich müssen auch zahlreiche Bezahlsysteme sinnvoll und sicher in die Webseite integriert werden. Cortex Tickets unterstützt inzwischen die reibungslose Zahlung mit Visa & Mastercard, Giropay und per Vorkasse.
  • Um den Arbeitsaufwand für unsere Mitarbeiter möglichst gering zu halten, mussten auch viele automatisierte Prozesse entwickelt werden, zum Beispiel die Auszahlung an den Veranstalter oder die Abgleichung der Vorkassenzahlungen. Cortex Tickets ist in der Lage viele dieser Prozesse völlig ohne menschliches Eingreifen automatisiert durchführen.
Die Verkaufsseite eines Beispielevents

Die Verkaufsseite eines Beispielevents

Ein besonders Augenmerk wollten wir auch auf möglichst einfache Einlasskontrollen legen, denn was nützt einem Veranstalter der beste Onlinevorverkauf, wenn später am Einlass Chaos entsteht. Wir haben dieses Problem durch eine Kombination aus klassischer Einfachheit und moderner Innovation gelöst:

Ganz klassisch kann der Veranstalter sich bei uns eine Gästeliste ausdrucken und die einzelnen Besucher abhaken. Er hat jedoch auch die Möglichkeit über den auf dem Ticket integrierten Barcode eine Einlasskontrolle mit einem Barcode-Scanner und unserer modernen Multiplattform-Software durchzuführen.

Als besondere Innovation bietet Cortex Tickets auch die Einlasskontrolle per iPhone und Android an. Hier benötigt der Veranstalter nur die kostenlose Cortex Tickets App und kann mit der integrierten Kamera des eigenen Smartphones die Kontrolle vornehmen.

Einlasskontrolle per Barcodescanner und Cortex Tickets Software

Einlasskontrolle per Barcodescanner und Cortex Tickets Software

Einlasskontrolle per iPad

Einlasskontrolle per iPad

Heute ist Cortex Tickets mit einem Jahresumsatz von über 100.000 € und mehreren hundert Events pro Woche eine etablierte und beliebte Ticketplattform, die sich mit den fairen und günstigen Preisen hinter keinem Konkurrenten verstecken muss.

In Unsere Projekte veröffentlicht

Fortschrittliches Testing von iPhone Apps mit UIAutomation

Das gründliche Testen von iPhone Apps kann eine große Herausforderung sein. Besonders durch den kurzen Update Zyklus, sowohl von Entwicklerseite (regelmäßige Updates der eigenen App) wie auch von Apple (Veröffentlichung neuer iOS Versionen mit teils großen Änderungen an der UI) werden notwendige Tests oft zu einer sehr mühsamen und repetitiven Aufgabe.

Mit UIAutomation hat Apple deshalb ab iOS 4.0 eine Möglichkeit geschaffen, automatisierte Tests durchzuführen und dem Entwickler somit viel Arbeit zu sparen. Der Grundgedanke von UIAutomation ist dabei, bei den Tests tatsächlich die UI der App anzusprechen (d.h. es werden tatsächliche Touches simuliert), und nicht wie in klassischen Testverfahren Methoden zu schreiben, welche nur den Sourcode der App ansprechen. Es gibt somit keinen Bruch zwischen den Testfällen und der späteren tatsächlichen Bedienung durch den Benutzer.

UIAutomation ist Teil des Instruments Sets von XCode und kann sowohl Apps auf dem iPhone, iPad aber auch im iPhone Simulator automatisch bedienen. Die Tests selbst werden dabei in Javascript geschrieben und greifen auf die Accessibility Eigenschaften der UI Elemente der App zu.

Im Folgenden werden wir anhand eines kleines Testprogramms eine kurze Einführung in UIAutomation geben und demonstrieren, warum wir bei fast allen unseren Apps im Testing darauf bauen.

Erstes Beispiel in UIAutomation anhand einer Beispiel App

Erster TableViewControllerZweiter TableViewController

Für unser Beispiel haben wir eine rudimentäre Fussballverwaltungs-App implementiert. Sie besteht aus zwei TableViewController, welche einmal die angelegten Vereine und einmal die angelegten Spieler darstellen. Zwischen den Controllern kann über eine TabBar gewechselt werden. Außerdem kann man in der App einen Verein hinzufügen und Spieler löschen.

Erstellen eines neuen Skripts in Instruments

Erstellen eines neuen Skripts in Instruments

Als erstes wollen wir automatisch in das Tab „Spieler“ wechseln und dort an eine bestimmte Position scrollen. Dazu erstellen wir ein neues Skript (siehe Screenshot) und geben folgenden Code ein:

//Um Tipparbeit zu sparen, definieren wir uns hier zwei Hilfsvariablen
var target = UIATarget.localTarget();
var app = target.frontMostApp();

/*
* Es ist zu empfehlen, einzelne Tests in Funktionen auszulagern. 
* Dies steigert die Übersichtlichkeit und ermöglicht es einzelne Tests seperat durchzuführen
*/
function scrollToPlayer(){
    app.tabBar().buttons()["Spieler"].tap();
    app.mainWindow().tableViews()[0].scrollToElementWithPredicate("name beginswith 'Oliver Baumann'");
}

//An dieser Stellen rufen wir unsere definierte Testfunktion auf
scrollToPlayer();

Jedes UIKit Element der App wird in Javascript durch ein zugehöriges UIAElement repräsentiert und findet sich, je nach Position in der jeweiligen View, auf einer gewissen Position in der View Hierarchie wieder. Wenn auf ein bestimmtes Element zugegriffen werden soll, muss in der View Hierarchie zum entsprechenden Element hin abgestiegen werden. In unserem Beispiel greifen wir zum Beispiel auf die TabBar unser App zu, holen uns aus der View Hierarchie alle Schaltflächen der TabBar und weisen UIAutomation an, auf den Button mit der Bezeichnung „Spieler“ zu klicken.

Beispielhafte View-Hierarchie

Beispielhafte View-Hierarchie

Genauso selektieren wir die erste (und in diesem Fall einzige) TableView unserer View Hierarchie und weisen UIAutomation an, zu einer bestimmten Zelle dieser Tabelle zu scrollen. Wir können diese Zelle mit einem einfachen Predicate auswählen oder auch direkt bestimmen, zu welcher Zelle gescrollt werden soll.

Sinnvolles Logging und das Definieren von Testfällen

In unserem bisherigen Beispiel musste der Benutzer selbst am Handy überprüfen, ob das Gerät die gegebenen Anweisungen richtig ausgeführt. Dies natürlich nicht die Idee von automatischen Tests. Stattdessen wollen wir nun im nächsten Schritt Testfälle definieren, Logging-Ausgaben einführen und automatisch Screenshots anfertigen.

Als unseren Testfall möchten wir dazu einen neuen Verein zur Liste hinzufügen. Dazu klicken wir zunächst auf den + Knopf in der Vereinsübersicht, tragen dann den Namen des neuen Vereins in das Textfeld ein, klicken auf Speichern und überprüfen danach ob die neue Zelle in der Tabelle vorhanden ist.

In diesem Bildschirm kann ein Verein hinzugefügt werden.

In diesem Bildschirm kann ein Verein hinzugefügt werden.

Im automatisierten Javascript-Code sieht das folgendermaßen aus:

var target = UIATarget.localTarget();
var app = target.frontMostApp();

function addClub(clubName){
    //Hiermit wird ein neuer Testfall angelegt und benannt
    UIALogger.logStart("Anlegen eines neues Vereins: "+clubName);

    //Wir navigieren auf die Anlege-Seite und füllen das Formular aus
    app.navigationBar().buttons()["Add"].tap();
    app.mainWindow().textFields()[0].setValue(clubName);
    app.mainWindow().buttons()["Speichern"].tap();

    //Wir loggen das erfolgreichen Ausfüllen des Formulars
    UIALogger.logMessage("Formular erfolgreich ausgefüllt");

    //Wir selektieren die (hoffentlich) neu angelegte Zelle
    var cell = app.mainWindow().tableViews()[0].cells().firstWithPredicate("name beginswith '"+clubName+"'");

    //Wir warten kurz bis die Übergangsanimation zwischen den Screenshots beendet ist
    target.delay(1);

    //Wir machen einen Screenshot der TableView um zu sehen ob unser unteres Ergebnis auch stimmt
    target.captureScreenWithName("Tabelle nach Anlegen");

    //Eine Zelle ist dann valid, wenn Sie in der TableView exisiert und somit wurde auch unser Test bestanden
    if(cell.isValid()){
        UIALogger.logPass("Anlegen eines neues Vereins: "+clubName);
    }else{
        UIALogger.logFail("Anlegen eines neues Vereins: "+clubName);	
    }
}

addClub("SC Freiburg");

Wir haben nun also einen Testfall definiert und können beim Ausführen des Skripts in Instruments den Test verfolgen und sehen, ob der Test erfolgreich war. Zu dem können wir uns im Nachhinein alle Testfälle mit Log-Ausgaben und Screenshots anschauen und so etwaige Fehler untersuchen.

Erfolgreiche Tests können in Instruments nachvollzogen werden.

Erfolgreiche Tests können in Instruments nachvollzogen werden.

Mit diesen Instrumenten ist uns nun möglich ein Set von Tests zu definieren, welche alle erfolgreich durchlaufen werden müssen, bevor wir ein Update der App freigeben. Ein Testen der App auch nach kleinen Änderungen ist somit ohne weiteren Aufwand möglich.

Kompliziertere Aktionen

Natürlich haben wir in unseren bisherigen Beispielen nur an der Oberfläche der Fähigkeiten von UIAutomation gekratzt. Einige besondere Eigenschaften und Fähigkeiten möchten ich an dieser Stelle noch hervorheben, da in diesen Fällen nochmals die Überlegenheit von UIAutomation zu handgeschriebenen Tests klar wird:

Behandlung von Alerts
Ein großes Problem bei händischen bzw. automatischen Tests ohne UIAutomation sind zufällig auftretende Alerts (z.B. weil das Handy einen Anruf oder eine SMS empfängt). UIAutomation kann diese Alerts automatisch schließen und fährt danach an der richtige Stelle mit dem Test fort. Der Test ist also nicht automatisch unbrauchbar, nur weil ein Alert aufgetreten ist. Dies ist besonders für zeitintensive automatische Tests sehr praktisch.

UIATarget.onAlert = function onAlert(alert){
    UIALogger.logWarning("Der Alert '" + alert.name() + "' hat die Ausführung unterbrochen!");
    return false; // use default handler
}

Zu dem können eigene Alert Handler programmiert werden, um mit auftretenden Alerts umzugehen. Zum Beispiel wenn ein Verein doppelt in die Liste eingetragen wird, könnte ein Alert von der App gefeuert werden und UIAutomation in einem solchen Alert-Handler dieses Verhalten behandeln.

Multitasking Tests
Mit der Einführung des Multitaskings und dem damit möglichen Wechsel zwischen den Apps sind einige Herausforderungen beim Testen hinzugekommen, da gestestet werden muss, ob die App auch den jeweiligen Screen bzw. die jeweilige Funktion nach dem kurzzeitigen Wechseln zwischen den Apps noch richtig ausführt. UIAutomation bietet hier eine einfache Möglichkeit Multitasking durch den Benutzer zu simulieren:

UIATarget.localTarget().deactivateAppForDuration(5);

Diese Zeile schickt die App für 5 Sekunden in den Hintergrund und fährt danach mit den Tests des programmierten Skripts fort.

Ändern der App-Orientation
UIAutomation kann auch die Rotation des iPhones simulieren und somit z.B. zwischen Landscape und Portrait Modus hin- und herschalten. Zu beachten ist dabei jedoch, dass die Bewegungssensoren des iPhones dabei nicht angesteuert werden, sondern das Drehen nur simuliert ist. Wenn man also auf Bewegungssensordaten angewiesen ist, reicht diese Methode für einen Test nicht aus.

var target = UIATarget.localTarget();
var app = target.frontMostApp();

target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_LANDSCAPELEFT);
UIALogger.logMessage("Das Telefon befindet sich im " + app.interfaceOrientation() + "Modus.");
target.setDeviceOrientation(UIA_DEVICE_ORIENTATION_PORTRAIT);

Multi-Touch
Bis jetzt haben wir nur einfach Taps auf dem Display ausgeführt. Viele Apps benutzen aber auch komplizierte Multi-Touch gesten zur Bedienung, und sei nur ein Swipe um einen Tabelleneintrag zu löschen. Natürlich bietet UIAutomation auch die Möglichkeit diese komplizierteren Gesten zu simulieren.
Zum Beispiel ist möglich die zwei Finger Zoom-Geste (sog. Pinch-Geste) über folgenden Aufruf durchzuführen:

UIATarget.localTarget().pinchOpenFromToForDuration({x:20, y:200}, {x:300, y:200},2);

Und eine Flick-Geste (zum Beispiel um ein Swip-To-Delete aufzurufen) wird folgendermaßen ausgeführt:

UIATarget.localTarget().flickFromTo({x:160, y:200}, {x:160, y:400});

Delay – Wenn’s mal länger dauert
Ein häufiges Problem beim Einsatz von UIAutomation ist die Einbeziehung von Wartezeiten, typischerweise weil eine Animation beendet werden soll oder gewartet werden muss, bis Daten aus dem Internet heruntergeladen worden sind. Ein einfach Hilfsmittel ist es hier, den Test einfach kurz warten zu lassen:

var target = UIATarget.localTarget();
target.delay(1);

Der Test wartet nun eine Sekunde, bevor er mit der weiteren Ausführung fortfährt.

Zusammenfassung und weiterführende Links

UIAutomation ist ein sehr mächtiges Werkzeug, welches das Testen von iPhone Apps unglaublich vereinfacht und auf eine neue Ebene gehoben hat. Es ist für jede halbwegs komplexe App zu Empfehlen Testfälle zu definieren und die Tests vor jedem Update durchlaufen zu lassen. Mit den Logging und Screenshot Funktionen ist auch eine Fehlersuche relativ intuitiv möglich.

Mehr über das UIAutomation findet man zum Einen natürlich bei Apple:

Aber auch in in einigen empfehlenswerten Blogs:

In Unsere Projekte veröffentlicht | Getaggt: , , , , ,