Mit Tests zurück in die Zukunft



Ähnliche Dokumente
Primzahlen und RSA-Verschlüsselung

Sich einen eigenen Blog anzulegen, ist gar nicht so schwer. Es gibt verschiedene Anbieter. ist einer davon.

icloud nicht neu, aber doch irgendwie anders

Informationsblatt Induktionsbeweis

Programmieren in Java

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

Softwaretests in Visual Studio 2010 Ultimate Vergleich mit Java-Testwerkzeugen. Alexander Schunk Marcel Teuber Henry Trobisch

Professionelle Seminare im Bereich MS-Office

Einrichtung des Cisco VPN Clients (IPSEC) in Windows7

CADEMIA: Einrichtung Ihres Computers unter Windows

Prozentrechnung. Wir können nun eine Formel für die Berechnung des Prozentwertes aufstellen:

Sicherheitseinstellungen... 2 Pop-up-Fenster erlauben... 3

FuxMedia Programm im Netzwerk einrichten am Beispiel von Windows 7

Was meinen die Leute eigentlich mit: Grexit?

Der Kalender im ipad

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Technische Analyse der Zukunft

iphone- und ipad-praxis: Kalender optimal synchronisieren

Jeopardy and andere Quizformate im bilingualen Sachfachunterricht Tipps zur Erstellung mit Powerpoint

Um zu prüfen welche Version auf dem betroffenen Client enthalten ist, gehen Sie bitte wie folgt vor:

Urlaubsregel in David

Durchführung der Datenübernahme nach Reisekosten 2011

Internet Explorer Version 6

Übung: Verwendung von Java-Threads

Carolo Knowledge Base

Java: Vererbung. Teil 3: super()

infach Geld FBV Ihr Weg zum finanzellen Erfolg Florian Mock

Handbuch zur Anlage von Turnieren auf der NÖEV-Homepage

Wordpress: Blogbeiträge richtig löschen, archivieren und weiterleiten

Erstellen eigener HTML Seiten auf ewon

Daten-Synchronisation zwischen dem ZDV-Webmailer und Outlook ( ) Zentrum für Datenverarbeitung der Universität Tübingen

Universal Dashboard auf ewon Alarmübersicht auf ewon eigener HTML Seite.

Tutorial -

UserManual. Handbuch zur Konfiguration einer FRITZ!Box. Autor: Version: Hansruedi Steiner 2.0, November 2014

.NET Code schützen. Projekt.NET. Version 1.0

Downloadfehler in DEHSt-VPSMail. Workaround zum Umgang mit einem Downloadfehler

YouTube: Video-Untertitel übersetzen

ICS-Addin. Benutzerhandbuch. Version: 1.0

DER SELBST-CHECK FÜR IHR PROJEKT

Guide DynDNS und Portforwarding

2. ERSTELLEN VON APPS MIT DEM ADT PLUGIN VON ECLIPSE

Studieren- Erklärungen und Tipps

geben. Die Wahrscheinlichkeit von 100% ist hier demnach nur der Gehen wir einmal davon aus, dass die von uns angenommenen

Komponententest. Testen von Software Systemen. Übung 02 SS 2009 Version:

Lizenzen auschecken. Was ist zu tun?

Objektorientierte Programmierung

40-Tage-Wunder- Kurs. Umarme, was Du nicht ändern kannst.

Anleitung über den Umgang mit Schildern

Zählen von Objekten einer bestimmten Klasse

Adminer: Installationsanleitung

Virtual Private Network

Leichte-Sprache-Bilder

Einführung in die Programmierung

Inhaltsverzeichnis. 1. Empfängerübersicht / Empfänger hinzufügen 2. Erstellen eines neuen Newsletters / Mailings 3. Versand eines Newsletters

Local Control Network Technische Dokumentation

Zeichen bei Zahlen entschlüsseln

Lineare Gleichungssysteme

SharePoint Demonstration

Artikel Schnittstelle über CSV

! " # $ " % & Nicki Wruck worldwidewruck

Anmeldung und Zugang zum Webinar des Deutschen Bibliotheksverbandes e.v. (dbv)

Installation der SAS Foundation Software auf Windows

AutoCAD Dienstprogramm zur Lizenzübertragung

HIER GEHT ES UM IHR GUTES GELD ZINSRECHNUNG IM UNTERNEHMEN

Anmeldung und Zugang zum Webinar des Deutschen Bibliotheksverbandes e.v. (dbv)

Web Interface für Anwender

Installation OMNIKEY 3121 USB

Ablaufbeschreibung für das neu Aufsetzen von Firebird und Interbase Datenbanken mit der IBOConsole

Pakete dienen dazu, die Software eines Projektes in größere inhaltlich zusammengehörige Bereiche mit eigenem Namen einzuteilen (siehe Java API).

Lehrer: Einschreibemethoden

MSDE 2000 mit Service Pack 3a

5.2 Neue Projekte erstellen

MORE Profile. Pass- und Lizenzverwaltungssystem. Stand: MORE Projects GmbH

TTS - TinyTimeSystem. Unterrichtsprojekt BIBI

Was ist Sozial-Raum-Orientierung?

GEORG.NET Anbindung an Ihr ACTIVE-DIRECTORY

Testen mit JUnit. Motivation

Datensicherung. Beschreibung der Datensicherung

In 12 Schritten zum mobilen PC mit Paragon Drive Copy 11 und Microsoft Windows Virtual PC

mysql - Clients MySQL - Abfragen eine serverbasierenden Datenbank

Einführung in die Informatik Tools

ONLINE-AKADEMIE. "Diplomierter NLP Anwender für Schule und Unterricht" Ziele

Persönliches Adressbuch

Windows XP Jugendschutz einrichten. Monika Pross Molberger PC-Kurse

2 Die Terminaldienste Prüfungsanforderungen von Microsoft: Lernziele:

SEP 114. Design by Contract

CADEMIA: Einrichtung Ihres Computers unter Mac OS X

Outlook. sysplus.ch outlook - mail-grundlagen Seite 1/8. Mail-Grundlagen. Posteingang

Lernerfolge sichern - Ein wichtiger Beitrag zu mehr Motivation

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

Alle gehören dazu. Vorwort

SANDBOXIE konfigurieren

PHPNuke Quick & Dirty

Version smarter mobile(zu finden unter Einstellungen, Siehe Bild) : Gerät/Typ(z.B. Panasonic Toughbook, Ipad Air, Handy Samsung S1):

Einführung in die objektorientierte Programmierung mit Java. Klausur am 19. Oktober 2005

Geld Verdienen im Internet leicht gemacht

Printserver und die Einrichtung von TCP/IP oder LPR Ports

Transkript:

im Oktober 2009 Eigentlich ist Testen ja eine einfache Sache: Man isoliert den zu testenden Teil mit Mocks, schafft sich ein Testfixture und los geht s. Was aber, wenn das Verhalten des Systems, welches unsere zu testende Komponente umgibt, nur teilweise bekannt ist? Dann basiert ein Mock nur auf Annahmen. Umso wichtiger wird der Integrationstest. Er muss die Funktionsfähigkeit unserer Komponente unter realen Bedingungen sicherstellen. Mit jeder Veränderung an unserer Komponente oder dem umgebenden System sollte ein Regressionstest durchgeführt werden. Verhält sich das System anders als im Mock angenommen, so muss korrigiert werden: unsere Komponente und natürlich den Mock.

Das konkrete Problem Getestet werden muss ein über längerer Zeit andauernder Prozess, bei dem die Zeit auch noch direkt in Berechnungen und Entscheidungen einfließt. Konkret wollen wir eine Erweiterung, einen Listener für das Issue-Tracking- System JIRA der Firma Atlassian erstellen. Dieser Listener reagiert auf Ereignisse aus dem System und soll diese Ereignisse protokollieren. Die zeitliche Abfolge und der zeitliche Abstand der Ereignisse spielt dabei eine wesentliche Rolle. Die klassische Lösung Ein Mock setzt immer an der Schnittstelle an, die die Daten in das zu testende System übergibt und ersetzt das daten-liefernde System. Für die üblichen Verdächtigen wie Dateien, Datenbanken oder Webservices gibt es bereits Lösungen, aber für die Zeit? Applikation unter Test Frameworks Java Standard Bibliotheken inkl. System.currentTimeMillies() Betriebssystem Datenfluss für die Zeit in einer Java-Anwendung Um ein System oder eine Komponente ersetzen zu können, brauchen wir erst einmal ein frei zugängliches (Java) Interface. Dies scheint für die Zeit nicht zu existieren. Aber was nicht da ist, kann man ja erschaffen. Wir könnten einen TimeService bereitstellen, der jedwede Zeit liefert, die in unserer Ap- Seite 2 von 7

plikation verwendet wird. Diesen TimeService kann man dann auf dem üblichen Weg mocken. Ein solches Vorgehen würde man wohl als Test Driven Design bezeichnen. In unserem konkreten Fall ist das nicht möglich, da uns der Zugriff auf das System verwehrt ist, wir haben nur Zugriff auf die von uns erstellte Erweiterung. Aber das Zusammenspiel dieser Erweiterung mit dem System ist gerade der für den Test interessante Punkt. Also muss eine andere Lösung her. Die nahe liegende, aber fatale Lösung Wenn wir die Zeit für unsere Applikation nicht beeinflussen können, warum verstellen wir die Zeit nicht gleich für das gesamte Betriebssystem? Dies ist technisch sicherlich möglich, aber würde fatale Folgen auf andere zeitabhängige Prozesse des Systems haben: Redo-Logs der Datenbanken können durcheinander geraten, Backup-Prozesse ebenso, Netzwerk-Dateisysteme oder Timeout-Prozesse im Allgemeinen sowie Log-Rotation geraten aus dem Tritt. Von diesem Vorhaben sollten wir also Abstand nehmen. Die solide Lösung Wenn wir uns mit klassischen, programmiertechnischen Methoden nicht in den Datenfluss der Zeit schalten können, müssen stärkere Geschütze aufgefahren werden. Die Verwendung Aspektorientierter Programmierung für das Mocken ist nicht neu, aber ist sie auch hier anwendbar? Ja, es ist möglich, aber wir werden einige Hürden umschiffen müssen: Bei der näheren Analyse des Problems fällt auf, dass die entscheidende Methode, java.lang.system.currenttimemillies() natürlich einer Standard Core-Klasse angehört und zudem noch eine native Methode ist. Das heißt, ihre Implementierung liegt nicht in Java Bytecode vor und ist somit für gängige AOP Frameworks nicht erreichbar. Um die Implikationen unseres Vorhabens besser verstehen zu können, führen wir uns noch einmal einige Grundlagen der AOP vor Augen. Für die konkrete Ausführung wurde hier AspectJ gewählt. AOP verändert meist den Bytecode existierender Java Klassen. Dies kann auf unterschiedliche Art und Weise geschehen. Man unterscheidet static, loadtime und runtime weaving. Loadtime und runtime weaving sind elegant, da sie seit Java5 lediglich den Start der JVM mit einem zusätzlichen Parameter (- Seite 3 von 7

javaagent:) erfordern. Weitere Modifikationen an der Laufzeitumgebung sind nicht nötig. Sie benötigen allerdings einen modifizierten Classloader, um den Bytecode der zu ladenden Klassen verändern zu können. Der Classloader für Standard Core Klassen wie java.lang.system lässt sich allerdings, schon aus Sicherheitsgründen, nicht so einfach modifizieren. Daher kommt hier nur statisches Weaving in Frage. Das statische Weaving verändert den Bytecode, bevor er auf den Klassenpfad gestellt wird. In unserem Fall heißt das, dass das rt.jar des JRE verändert wird und dieses veränderte JAR das Original ersetzen muss. Das Verändern (Weaving) übernimmt das Tool ajc, das möglichst minimal-invasive Ersetzen des Originals erreichen wir dann durch den JVM Parameter -Xbootclasspath/p:. Dieser Parameter erlaubt es uns, Klassen zuvorderst auf den Klassenpfad zu legen, so dass sie beim Bootstrapping der JVM als erstes geladen werden und die Originalklassen nicht mehr zum Zuge kommen: java -Xbootclasspath/p:timejump_rt.jar:aspectjrt.jar -Dorg.ct42.timejump=3600000 org.ct42.timejumptest Es wird das modifizierte rt.jar und die AspectJ Runtime auf den Bootclasspath gelegt. Mit dem Setzen des System Properties reisen wir eine Stunde in die Zukunft und führen unser kleines Testprogramm aus. Die AspectJ Implementierung unseres Aspektes sieht wie folgt aus: package lib; public aspect TimeJump { long around(): call(long long System.currentTimeMillis()) { long timejump = Long.getLong("org.ct42.timejump", 0L).longValue(); long timestamp = proceed(); return timestamp + timejump; Die Implementierung ist recht schlicht gehalten. Dies hat gute Gründe, denn Seite 4 von 7

der Bootstrapping-Prozess der JVM ist ein sensibler Prozess: Sollten wir zum Beispiel versuchen, eine Log-Ausgabe über System.out abzusetzen, so werden wir feststellen, dass dies mit einer NullpointerException quittiert wird. Die Methode System.currentTimeMillies() wird nämlich sehr früh im Bootstrapping Prozess angesprungen, zu einer Zeit, zu der noch nicht einmal die Standard IN/OUT Streams initialisiert sind. Den Zeitsprung steuern wir über ein System Property, welches die Differenz zur Realzeit in Millisekunden angibt. Unser kleines Testprogramm sieht wie folgt aus: package org.ct42; import java.text.simpledateformat; import java.util.date; public class TimeJumpTest { public static void main(string[] args) { SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date today = new Date(); System.out.println( formatter.format(today) ); System.out.println( formatter.format( new Date( System.currentTimeMillis() ) ) ); Bringen wir es zur Ausführung, werden wir feststellen müssen, dass die erste Log-Ausgabe mit korrigierter Zeit erfolgt, die zweite allerdings nicht. Die Erklärung dafür führt uns wieder ein wenig in die Tiefen der AOP: Wie schon erwähnt, ist die Methode currenttimemillies() eine native Methode und kann nicht durch Weaving verändert werden. Es ist also nicht möglich, den Rückgabewert der Methode zu verändern, bevor er diese verlässt. Stattdessen müssen wir jeden Aufruf der Methode verändern und den Rückgabewert anpassen, bevor er im Laufe des Programms weiter verwendet wird. Dies erledigt der around() Aspekt für uns. Allerdings haben wir ja nur ein statisches Weaving angesetzt und damit nur das rt.jar verändert. Unsere Testklasse verwendet noch den original Rückgabewert. Dies würde auch auf alle anderen Aufrufe aus nachgeladenen JARs von Frameworks bzw. Bibliotheken Dritter zutreffen. Damit wir nun nicht umständlich alle weiteren Klassen und JARs statisch weaven müssen, werden wir hier das loadtime Weaving anwenden. Dafür muss eine Konfiguration in META-INF/aop.xml hinterlegt werden: Seite 5 von 7

<aspectj> <aspects> <aspect name="lib/timejump" /> </aspects> </aspectj> Unser JVM Aufruf sieht dann so aus: java -Xbootclasspath/p:timejump_rt.jar:aspectjrt.jar:timejumpaspects.jar javaagent:aspectjweaver.jar -Dorg.ct42.timejump=3600000 org.ct42.timejumptest Die Konfiguration legen wir praktischerweise mit in das timejumpaspects.jar. Nun sollten beide Log-Ausgaben einer Zeitreise unterliegen. Applikation unter Test Frameworks loadtime weaving Java Standard Bibliotheken inkl. System.currentTimeMillies() Betriebssystem static weaving Weaving Punkte und Typen Seite 6 von 7

Die Zeit steuern Das obige einfache Beispiel erzwingt die Zeitreise einmalig beim Start des Programms. Nun wollen wir unseren Test allerdings im Zeitraffer durchlaufen. Dazu müssen wir nur das Systemproperty org.ct42.timejump auch zur Laufzeit verändern können. Dies geschieht am besten mit Bordmitteln des umgebenden Systems. Möglich wäre eine JMX Konsole oder ähnliches. In unserem Fall bedienen wir uns eines Jelly Scripts, für das JIRA eine Möglichkeit der Ausführung anbietet. Einschränkungen Der around() Aspekt ist recht laufzeitintensiv. Da die currenttimemillies() Methode oft auch für das Profiling herangezogen wird, sollte man bei Anwendung unseres TimeJump Aspekts Verfälschungen von Profiling-Ergebnissen erwarten dürfen. Das loadtime Weaving ist vom Classloader abhängig, sollte die Methode currenttimemillies() aus Code heraus angesprungen werden, der auf irgendeine Weise am Standard-Classloader vorbei geladen wurde, fehlt natürlich unser Aspekt. Das könnte zu einigem Durcheinander in der Applikation unter Test führen. Aber auf die Zeit kann auch in anderen Systemteilen zugegriffen werden, die nicht durch den Aspekt beeinflusst sind. Da wäre zum Beispiel die Verwendung der Funktion SYSDATE in SQL-Statements oder Zeitangaben in Daten, die die Applikation von außen über Dateien, Datenbanken oder Webservices erreichen. Hier müssen dann die entsprechenden Mocks mit der Zeitreisenkonfiguration unseres TimeJump Aspekts synchronisiert werden. Wir müssen auch mit Nebeneffekten rechnen. Zum Beispiel wird wohl die Websession ausgelaufen sein, nachdem wir den Zeitraffer aktiviert haben und uns weit in die Zukunft begeben. Seite 7 von 7