Quantcast
Channel: Agilität – codecentric AG Blog
Viewing all 129 articles
Browse latest View live

Code Reviews 2.0 mit Gerrit, Git und Jenkins

$
0
0

Sourcecode-Reviews gehören in unseren Projekten immer häufiger zur Definition of Done. Das  kontinuierliche Überprüfen von Source-Code ist aber oft aufwendiger als man eigentlich annimmt. Der Review wird nicht selten erst durchgeführt, wenn der Source-Code bereits im produktiven Repository eingecheckt ist. Wenn dann aufgrund der Reviews noch Änderungen notwendig sind, ist das sehr aufwendig. Es bleibt einem dann nichts anderes übrig als die Änderungen von Hand aus dem Repository zu entfernen und zu checken, ob noch alles wie gewünscht funktioniert. Da lohnt es sich doch mal einen Blick auf Gerrit zu werfen, ein auf Git basierendes Code-Review-System.

Was ist Gerrit?

Gerrit ergänzt das Versionskontrollsystem Git um einen Review Prozess, mit dem Sourcecode-Änderungen im Team „gereviewed“ werden können. Jede Änderung wird dabei zunächst einmal Gerrit bekanntgemacht, dann wird ein Review-Prozess gestartet und erst nach dessen Beendigung passiert dann das Hochladen in den offiziellen Quell-Code (Abbildung 1). Auch der CI-Server Jenkins kann an dem Workflow beteiligt werden, dann wird bereits vor dem eigentlichen Review die Änderung validiert, so hat der Reviewer schon mal eine Vorabinfo über die Qualität der Anpassung.

 

Abbildung 1

Aufbau von Gerrit

Gerrit funktioniert wie eine Art Proxy vor dem ebenfalls bereitgestellten Git-Server. Eingecheckt wird einfach in das Git-Repository und schon wird der Review-Workflow gestartet. Danach kann der Source-Code per Browser überprüft werden. Auch das direkte Einchecken bzw. Abbrechen von Reviews ist bei ausreichender Berechtigung möglich. Das „Review-Öko-System“ besteht aus einer Web-Oberfläche, Git-Server mit HTTP- sowie SSH-Schnittstelle und ab Version 2.5 einer Rest-Schnittstelle.

Aufsetzen von Gerrit

Gerrit ist gut dokumentiert, zum schnellen Einstieg bekommt man den ersten Test-Server ohne komplizierte Authentifizierung schnell installiert. Nach der Installation kann man dann über den Browser Projekte anlegen, per Git Reviews starten und diese dann per Browser durchführen.

Nach erfolgreichem Login kann man über Projects->Create New Project ein neues Projekt anlegen, damit wird ein Gerrit-Projekt inkl. Git-Repository angelegt. Mehr Details zur Installation und Konfiguration von Gerrit findet sich in der Gerrit-Doku.

Änderungen Gerrit bekanntmachen

Per Konvention werden alle Änderungen für einen bestimmten Branch nach refs/for/<branchname> gepushed, dann startet automatisch der Reviewprozess. Wenn die Überprüfung abgeschlossen ist, wird die Änderung in den Zielbranch gemerged. Falls keine Konflikte bei der Zusammenführung auftreten passiert dies automatisiert.

Jetzt aber los: Ein kleines Beispielprojekt

Das Beispielprojekt habe ich bereits in Gerrit angelegt, also kanns losgehen. Dazu benötigen wir nur eine Kommandozeile, die Git spricht.

Als erstes klonen wir das Repository, fügen ein Datei hinzu (test.txt) und pushen diese nach refs/for/master:

Gerrit hat die Änderung bereits registriert:

Nach klick auf die Änderung sehen wir mehr Details, u.a. Basisinformationen, Review-Status, Abhängigkeiten, beteiligte Dateien und Kommentare. Außerdem wurde die Änderung bereits validiert, das hat Jenkins bereits gemacht (Infos zur Jenkins-Konfiguration hier):

Jenkins hat dazu ebenfalls Kommentare hinterlassen:

Als nächstes schauen wir uns die Änderung einmal genauer an, dazu klicken wir einfach auf die Datei text.txt und die Änderungen werden angezeigt:

Kommentare zur Änderung werden einfach per Doppelklick verfasst:

Zum Abschluss müssen wir dem Review jetzt nur noch unser Gesamturteil verpassen. Dazu gehen wir zurück zur Änderung und klicken auf Review.

Das Urteil wird nach folgendem Punktesystem vergeben:

  • +2: Änderung genehmigt und kann in den Zielbranch gemerged werden
  • +1: Änderung gefällt, sollte aber noch von einem weiteren rereviewed werden
  • 0: keine Meihnung
  • -1: Änderung gefällt nicht so sehr, sollte also nicht gemerged werden
  • -2: Änderung abgelehnt und kann auf keinen Fall in den Zielbranch gemerged werden

Im Standard-Workflow werden +1, -1 als Empfehlungen, während +2,-2 als Genehmigung bzw. Ablehnung verstanden werden. Die Punktevergabe kann man in Gerrit berechtigen (Berechtigungs-Doku).

Ein Klick auf Publish veröffentlicht das Ganze, bei ausreichender Berechtigung kann die Änderung auch direkt gemerged werden, Publish and Submit. Wir submitten direkt.

gerritBrowser4

Die Änderung wird direkt in den Zielbranch (hier: master) gemerged und nochmal in der Übersicht angezeigt:

gerritBrowser5

 

Weitere Features von Gerrit

  • Berechtigung von Repository- sowie Review-Funktionen, beispielsweise können bestimmte Benutzer den Review-Prozess auch überspringen und direkt in den Zielbranch pushen. Das macht Gerrit sehr flexibel, im einfachsten Fall benutzt man Gerrit nur als Git-Server. Auch das Abbrechen eines Reviews ist möglich, wenn ein berechtigter Benutzer eine Änderung im Nachhinein direkt pushed, interpretiert Gerrit das als: Der Review scheint nicht mehr benötigt zu werden.
  • Replikation mit anderen Git-Repositories. Hiermit kann man seinen Workflow um entfernte Repositories erweitern. Denkbar wäre beispielsweise eine Integration von Git-Hub. Alle Änderungen, die für Gut befunden werden, werden automatisch repliziert.
  • SSH-Schittstelle: wie oben beschrieben bietet Gerrit auch eine SSH-Schnittstelle an. Diese ist nicht auf die Git-Features beschränkt, auch den Review selber kann man direkt per Shell machen (Gerrit-Commandline-Tools).
  • Eclipse-Integration: auch die Eclipse-Integration ist sehr ausgereift, sowohl das EGit-Plugin als auch ein Gerrit-Mylyn-Connector machen das Arbeiten mit Gerrit sehr einfach.

Fazit

Mein erster Eindruck: Gerrit kann was :-) Besonders die Integration mit Git ist sehr gelungen, man braucht außer Git nicht viel zu kennen. Man checkt einfach ein und schon wird der Review-Prozess gestartet. Auch die Anwendung mit Eclipse ist beeindruckend und wird sich vermutlich noch verbessern, da Eclipse selber auch Gerrit verwendet (Eclipse-Gerrit).

Die Integration mit Jenkins macht ebenfalls Spaß, es wird direkt der richtige Code ausgecheckt und validiert. Es ist nicht mehr notwendig mehrere Jobs für ein und dasselbe Projekt zu konfigurieren. Das Jenkins-Plugin sucht sich selbstständig das zu prüfende Changeset im richtigen Branch heraus.

Einschränkend muss man nur sagen, dass Gerrit nur mit Git funktioniert. Man hat ja nicht in jedem Projekt die freie Wahl des Versionskontrollsystems :-( Wenn man allerdings bereits mit Git arbeitet, ist der Weg zu Gerrit nicht mehr allzuweit.

Aus Projektsicht muß man sich natürlich fragen, will ich wirklich für jede Änderung an der Code-Basis ein Review durchführen? Der Vorteil von Gerrit ist, dass es nicht zum Reviewen verpflichtet, sondern auch als Git-Repository verwendet werden kann – wie für den schnellen Einstieg gemacht :-)

 


Eine Idee wächst

$
0
0

vom Open Space Software Development zum Agile Code Camp

Im letzten Jahr auf der Unconference ALE2012 in Barcelona haben wir das Open Space Software Development (#OSSWDEV) durchgeführt.  Während der drei Konferenztage entwickelten die Teilnehmer eine Conference App für die Konferenzgäste. Dabei wurde mindestens einmal die Stunde eine neue Version ausgeliefert und die Benutzer beeinflussten mit ihrem Feedback unmittelbar die weitere Entwicklung. Ziel dieses Experiments war es, zu Beobachten, welche agilen Methoden und Praktiken dem stressigen Umfeld und dem hohen Lieferdruck standhielten und welche nicht (siehe auch den zusammenfassenden Blogartikel).

Neben dem eigentlichen Experiment ist eine sehr inspirierende und intensive Lernumgebung entstanden. Die unterschiedlichen Teilnehmer sammelten in den Bereichen, in denen sie aktiv waren, intensive Erfahrungen und bauten neues Wissen auf.

Sowohl Teilnehmer als auch Interessierte die das #OSSWDEV verpasst haben, äußerten den Wunsch, an einer Wiederholung des Events teilzunehmen. So fand das Ganze im selben Format auf den XPDays Germany in Hamburg noch einmal statt. Auf Grund des guten Feedbacks entstand bei codecentric die Idee, aus dem Format des #OSSWDEV einen eigenständiges Event in einem größeren Rahmen und mit dem Fokus auf die Weiterbildung zu konzipieren.

So planen wir das Agile Code Camp im Raum Düsseldorf/Köln durchzuführen. Wir planen dort mit bis zu 100 Teilnehmern zusammen 5 Tage lang eine Anwendung zu entwickeln. Neben agilen Methoden und Praktiken wie z.B. Userstories, Testautomatisierung und Code-Metriken werden auch die neusten Technologien wie Vaadin, Spring, BigData Technologien und Gradle angewendet.

Um in den fünf Tagen den kompletten Software Entwicklungsprozess iterativ durchzuführen, d.h. vom Aufnehmen der Anforderung bei den Stakeholdern bis zur Veröffentlichung der lauffähigen Software, sind als Teilnehmerkreis nicht nur Entwickler sondern auch Tester, Product Owner und Agile Coaches eingeladen.

Experten werden während des gesamten Events die Teilnehmer dabei unterstützen lauffähige und produktionstaugliche Software zu erstellen. So bereiten wir einstündige Sessions als Einführung zu unterschiedlichen Themen vor, die je nach Bedarf der Teilnehmer durchgeführt werden. Aktuell planen wir zu folgenden Themen Sessions:

TDD, Unit Tests und Mocking Puppet
Akzeptanztests mit JBehave und Selenium Gradle
Versionskotrolle mit GIT Vaadin
Code Metriken mit Sonar Testen von Android Apps
Spring Testen von iOS Apps
Continuous Delivery mit Jenkins Agiles Anforderungsmanagement
Retrospektiven Techniken MongoDB

Solltet Ihr ein Thema haben das Eurer Meinung nach ebenfalls sinnvoll ist, würde ich mich über die entsprechende Anregung freuen.

Habt Ihr Lust in einer intensiven Woche mit vielen Leuten Software zu entwickeln und dabei neue Ideen und Technologien kenn zu lernen? Dann registriert Euch hier.

Wir beantworten auch gerne Eure Fragen und freuen uns über jede Anregung. Kontaktiert uns einfach über agilecodecamp@codecentric.de

Agile Code Camp – Web-Technologiestack

$
0
0

Vom 24.-28.06.2013 findet das erste codecentric Agile Code Camp statt. Wir bieten maximal 100 Teilnehmern die Gelegenheit, sich umfassend über alle wichtigen Bereiche agiler Softwareentwicklung zu informieren.

Agile Code Camp

Agile Code Camp 24.-28.06.2013

Erfahrene Softwareentwickler, die mehr über die Praktiken der Software Craftsmanship Bewegung erfahren wollen, werden genauso begeistert sein wie Produkt Owner und Scrum Master, die ihren Werkzeugkasten erweitern möchten. Auch für IT-Leiter, die erfahren möchten, ob agile Methoden auch in ihrem Unternehmen sinnvoll einsetzbar sind und wie diese in der Praxis funktionieren, wird eine Menge geboten: Die codecentric Experten stehen eine ganze Woche zur Verfügung, um alle Fragen rund um agile Projektabwicklung und Technologien zu beantworten.
An einem Beispielprojekt arbeiten ca. 10 Teams parallel. Innerhalb der Teams beschäftigen sich die Entwickler je nach Interesse mit einem bestimmten Schwerpunktthema. Die Schwerpunktthemen richten sich z. B. nach den eingesetzten Technologien wie Web-, iOS- und Android Entwicklung oder auch den Backend Technologien MongoDB, Hadoop und Neo4J. Die vielen an einem gemeinsamen Projekt arbeitenden Gruppen bieten zudem die Möglichkeit, sich detailliert mit Themen der Skalierung agiler Teams zu beschäftigen. Auch für Produkt Owner und Scum Master gibt es also genug zu tun. Parallel finden optionale Workshops zu all diesen Themen statt, in denen die theoretischen Grundlagen vermittelt werden.

Welche Inhalte werden den 100 glücklichen Teilnehmen geboten?

In einer Reihe von Beiträgen werden wir in den nächsten Wochen die von uns angebotenen Themenschwerpunkte vorstellen. Dieser Beitrag startet mit den Technologien, mit denen wir einen Web Client für unser Beispielprojekt implementieren. In weiteren Artikeln werden wir die anderen verwendeten Technologien und weitere Themen wie z.B. Workshops zu den agilen Praktiken oder unsere Continuous Deployment Pipeline vorstellen.

Was erwartet einen Entwickler im Agile Code Camp?

Zuerst einmal ganz viel Zeit zum hacken :)
Bringt also auf jeden Fall euren Laptop mit. Um die Einrichtungszeit zu minimieren haben wir eine funktionsfähige Entwicklungsumgebung bereits vorbereitet. Diese basiert auf Eclipse (bzw. STS) und enthält bereits ein lauffähiges Projekt als “Walking Skeleton”. Wenn ihr Eclipse nicht mögt ist das auch kein Problem. Benutzt einfach die IDE eurer Wahl und holt euch die Sourcen des Projekts aus unserem GitHub Account. Ihr kennt GitHub nicht und habt auch noch nie mit einem verteilten Versionskontrollsystem gearbeitet? – Auch kein Problem! Dann ist das Code Camp die beste Gelegenheit, Erfahrungen zu sammeln. Nehmt einfach zu Beginn an unserem Git Workshop teil, lernt dort die Grundlagen verteilter Versionskontrollsysteme und erprobt das erworbene Wissen direkt im Anschluss im Projekt.

Bei der Auswahl der Technologien zur Implementierung des Web Frontend haben wir uns an etablierte und aus unserer Sicht weit verbreitete Technologien aus dem Java Umfeld gehalten. Die Applikation verwendet Vaadin und Spring und wird mit Gradle gebaut. Im Backend nutzen wir einen JBoss Application Server und MongoDB als zentralen Datenspeicher. Damit wir die mobilen iOS und Android Clients sinnvoll anbinden können, wird die Logik im Backend über ReST Services zentral für alle Clients zur Verfügung gestellt. Wenn es darum geht, Statistiken und Reports zu erstellen, greifen wir auf Hadoop zurück. Für den Fall, dass ihr euch für Neo4J interessiert, können wir auch dazu einen Workshop und praktische Übungen anbieten.
Generell gilt, dass wir zu den eingesetzten Technologien auch entsprechende optionale Workshops anbieten, um euch den Einstieg zu erleichtern. Euren ersten praktischen Erfahrungen in der Welt von NoSQL steht also nichts mehr im Weg.

Warum Java?

Warum haben wir für das Web Frontend vermeintlich langweilige Technologien gewählt und nicht irgendwas Neues und Interessantes genommen? – Ganz einfach: Die Technologien sind eigentlich nicht so wichtig! – Im Agile Code Camp soll es für Entwickler vor allem darum gehen, den Umgang mit agilen Praktiken zu erlernen bzw. zu verbessern. Wir werden uns also im Detail mit Praktiken wie TDD, ATDD und Refactoring beschäftigen. Wenn wir dann die ersten mit JBehave und Selenium automatisierten Akzeptanztests umgesetzt haben, werden wir eine Continuous Delivery Pipeline mit mehreren Stages aufbauen. Die Stages erlauben uns, kontinuierlich unsere Akzeptanz- und Regressionstests auszuführen und unsere Applikation auf Knopfdruck nach Produktion zu liefern.
Natürlich werden in euren Projekten unter Umständen ganz andere Technologien verwendet. Das ist auch kein Problem, da sich die im Agile Code Camp gelernten Verfahren sehr einfach auf andere Technologiestacks übertragen lassen. Wir haben uns für Java entschieden, weil es eine weit verbreitete Sprache ist, anhand der sich agilen Praktiken sehr gut erklären lassen.

Was passiert noch?

Parallel zu den Aktivitäten in den Entwicklungsteams beschäftigen sich die Product Owner mit der Definition des gemeinsamen Backlog. Sie stimmen mit unserem Kunden die Anforderungen ab und organisieren gemeinsam mit den Scrum Mastern die Planungs- und Review Termine.

Ach ja, der Kunde!

Der Kunde ist ein wichtiger Teil eines agilen Projekts. Im Agile Code Camp haben wir einen echten Kunden, der echte Anforderungen und den Bedarf an einem funktionierenden Produkt mitbringt. Nach Abschluss des Camps sollen die Ergebnisse als Open Source Software produktiv genutzt werden können. Der Erfolgsdruck eines echten Projekts ist also auch im Agile Code Camp gegeben.
Falls ihr neugierig seit, wer unser Kunde ist und welches Produkt wir entwickeln, dann schaut einfach regelmäßig hier im Blog vorbei. Bis dahin würde ich mich über euer Feedback freuen: Welche Themen interessieren euch besonders? Worauf sollen wir eurer Meinung nach die Schwerpunkte legen und was gehört gar nicht in die Veranstaltung? Wünscht ihr euch einen Workshop zu einem bestimmten Thema? – Lasst es uns wissen.

Noch ein Hinweis zum Schluss

Wir haben nur eine beschränkte Anzahl an Plätzen und seit dieser Woche ist die Anmeldung auf unserer Webseite freigeschaltet.

Agile Code Camp – Stakeholder und Produktvision

$
0
0

Auf dem ersten codecentric Agile Code Camp bieten wir Euch die Möglichkeit, Erfahrung in der agilen Entwicklung von Software unter Einsatz modernster Technologien zu sammeln, in dem Software für einen echten Anwendungsfall, den ein echter Kunden hat, entwickelt wird. In diesem Blogeintrag stelle ich Euch kurz unsere Stakeholder und deren Produktvision vor

Stakeholder

AEGEE (Association des Etats Généraux des Etudiants de l’Europe / European Students’ Forum) ist eine Studentenorganisation mit dem Ziel, ein vereintes Europa zu schaffen. Dazu fördert und koordiniert AEGEE Kommunikation zwischen und Integration von Jugendlichen in Europa. Als politisch unabhängige und gemeinnützige Nicht-Regierungsorganisation (NGO) steht sie Studenten und Jugendlichen aller Fakultäten und Disziplinen offen. Derzeit hat AEGEE 13.000 Mitglieder aus fast 200 Universitätsstädten in 40 europäischen Ländern.

„The Arc” ist ein gemeinnütziges Trainingsprojekt für EU Jugendorganisationen unter dem Dach von AEGEE. Ziel des Projektes ist es u.a., die Qualität von ehrenamtlichen Jugendprojekten zu steigern, in dem junge Ehrenamtler aus ganz Europa in die Lage versetzt werden, die soziale Wirkung ihrer Projekte zu messen (sog. Social Impact Measurement). Die Idee dahinter: What gets measured gets valued! In allen Ländern Europas ist es seit der Finanzkrise bedeutend schwieriger für Jugendorganisationen geworden, Gelder für ihre Projekte zu akquirieren. Nur wenn Organisationen den gesellschaftlichen Nutzen ihres Tuns nachweisen (und gleichzeitig besser in dem werden, was sie tun), werden öffentliche und private Geldgeber langfristig bereit sein, auch weiterhin in ehrenamtliche Projekte zu investieren.

Das von The Arc eingesetzte Tool, mit dem Jugendorganisationen ihre soziale Wirkung messen können, heißt IOOI. Diese Methode wird seit Jahren bspw. in Großbritannien und Irland verwendet und in Deutschland u.a. von der Bertelsmann Stiftung sowie der PricewaterhouseCoopers AG WPG eingesetzt. Die Methode wird derzeit durch ein Workbook unterstützt.

Produkt Vision

Ziel des zu entwickelnden Produkts ist es, die Methode mit Hilfe einer Web-Applikation zu unterstützen. Dabei bietet das Produkt folgende wesentliche Vorteile:

  • Automatische Generierung von Umfragen
    Social Impact Measurement basiert derzeit v.a. auf der Durchführung von Umfragen bzw. auf Auswertung von statistischen Gesellschaftsindikatoren (wie z.B. Senkung der Jugendarbeitslosigkeit durch ein soziales Projekt). Mit der Papierversion des Workbooks müssen alle für Umfragen entwickelten Fragen aufwendig nach Zielgruppen und Umfragezeitpunkten gruppiert und immer wieder neu abgeschrieben werden. Die Web-Applikation soll diesen Prozess automatisieren.
  • Automatische Auswertung von Ergebnissen
    Auch Ergebnisse müssen derzeit manuell ausgewertet werden. Ein weiteres Ziel ist daher die Entwicklung eines „Controlling Cockpits”, in dem ehrenamtliche Leiter von sozialen Projekten die Entwicklung ihrer gesellschaftlichen Wirkung tagesaktuell oder zu Stichtagen mitverfolgen können.
  • Zugang der Entwickler von „The Arc” zu den Messstrategien der in der Applikation registrierten Projekte
    Soziale Wirkungsmessung ist gerade für kleine Organisationen oft ein Buch mit sieben Siegeln. Sofern das Team von „The Arc” jedoch Einblick in die Messstrategien von Einzelprojekten erhält, können kleine Projekte Coaching zu Themen erhalten, wie: „Welche Indikatoren funktionieren?”, „Wie steht mein Projekt im Vergleich zu anderen da?”, „Welche Indikatoren benutzen andere bzw. werden gerne von Sponsoren gesehen?”, „Wie kann ich meine Wirkung steigern?”, „Wie kann ich Social Impact Measurement als Marketingtool einsetzen?”, usw.

Eine weitergehende Unterstützung mit unterschiedlichen Features ist denkbar. Die Applikation soll zunächst innerhalb von AEGEE pilotiert und dann in verschiedenen anderen europäischen, nationalen oder lokalen gemeinnützigen Organisationen ausgerollt werden.

Anlegen und Verwalten von Projekten

Mithilfe der IOOI Methode kann die gesellschaftliche Wirkung sozialer Projekte gemessen werden. Über das Workbook können Basisdaten, Zielgruppen, Messziele und -strategien erhoben werden.

Umsetzungsplanung der Messstrategie

Im Rahmen der Messstrategie werden mehrere Indikatoren festgelegt, um die Ergebnisse und gesellschaftlichen Wirkungen des Projekts zu quantifizieren. Neben der Benutzung von statischen Daten aus öffentlich zugänglichen Quellen (statistische Ämter, EU usw.), spielen Befragungen eine wichtige Rolle bei der Messung. Daher soll es möglich sein, die Datenabfragen und Befragung zeitlich zu planen.
Dabei geht es darum

  • Wie wird der Indikator gemessen?
  • Wer liefert die Daten? bzw. Wer wird befragt?
  • Wann wird was gemessen?

Weiterhin sollte mit Hilfe der Anwendung die aktuellen Status der Messungen verfolgt werden. Dabei soll überwacht werden, welche geplanten Messungen aktuell anstehen (oder sogar überfällig sind), aber auch wie die aktuellen Antwortraten gestarteter Befragungen sind. Für den Nutzer (z.B. ein ehrenamtlicher Leiter eines sozialen Projektes) sollte somit eine Art “Controlling Cockpit” entwickelt werden, in dem er die Messergebnisse über den Projektverlauf nachvollziehen kann.

Messung durch Umfragen

Findet eine Messung in Form einer Umfrage statt. Kann diese mit Hilfe der Anwendung erstellt und an eine zusammengestellte Empfängergruppe versendet werden. Die Empfänger können an der Umfrage online über die Web-Applikation teilnehmen oder nutzen die Android oder iOS Applikation.

Eine Alternative wäre auch ein Fragebogen in Papierform auszudrucken und auszufüllen. Das Ergebnis kann dann entsprechend wieder in die Applikation eingegeben werden.

Messung durch statistische Daten

Hier sollte es möglich sein, die für die Messung geforderten statistischen Daten innerhalb der Applikation zu erfassen. Dabei können die Kennzahlen direkt eingegeben werden oder in einem Standardformat importiert werden.

Zu Klären ist, ob es standardisierte Schnittstellen zu öffentlichen Statistikinformationen gibt, über die die Anwendung online auf Daten zugreifen könnte.

Analyse und Auswertung

Die zu einem Projekt gesammelten Testdaten sollen ausgewertet und in nutzbarer Form dargestellt werden. Sinnvoll wäre ebenfalls eine neutrale Exportschnittstelle über die die Daten an ein Auswertungstool übergeben werden können.

Weiterhin interessieren sich die Mitglieder von „The Arc“ für projektübergreifende Nutzungsstatistiken, um zu prüfen inwieweit Projekte die Methode erfolgreich einsetzen und an welchen Stellen sie Schwierigkeiten mit der Wirkungsmessung haben.

Ich hoffe, dies gibt Euch einen Eindruck über den Anwendungsfall, an dem wir auf dem Agile Code Camp mit Euch arbeiten werden. Wie wir zur Idee für das Agile Code Camp gekommen sind könnt Ihr hier nachlesen. Den Überblick über einen der geplanten Technologiestacks hat Lars Rückemann in seinem Blogeintrag gegeben.

Agile Software Factory Boot Camp mit der FH Aachen

$
0
0

Product Backlog, Planning Poker, Daily Stand-up, Retrospective, Definition of Ready/Done – all das sind seit dieser Woche keine Fremdworte mehr für 19 Masterstudenten der Fachhochschule Aachen (Campus in Jülich), die sich in einem einwöchigen Praktikum, dem ASF Boot Camp, den Herausforderungen agiler Softwareentwicklung stellen.

Zusammen mit Prof. Dr. Bodo Kraft und Marc Schreiber von der Hochschule haben wir ein studienbegleitendes Modul entworfen, in dem die Studenten ihre Grundkenntnisse in agilen Methoden & Praktiken vertiefen und moderne Entwicklungswerkzeuge kennen lernen. Die Teilnehmer sind Studenten des Masterstudiengangs Technomathematik und haben vorab ein Duales Studium (Math.Techn. SW-Entwickler plus B.Sc.) an der FH Aachen absolviert. Das Modul ist mittlerweile offiziell akkreditiert und als Wahlbaustein fester Bestandteil des Studiengangs, es schließt mit einer hochschulinternen Prüfung ab.

Dem Praktikum vorgeschaltet ist eine 2tägige theoretische Einführung zu Scrum Basics, Designprinzipien und verteilter Versionsverwaltung. Seit Montag arbeiten sich nun die vier Teams “Team1″, “Work Minimizer”, “Shockwave Rebels” und “Frauenpower” durch das Product Backlog einer konkreten Anwendung und setzen in täglich 2 Sprints die geforderten User Stories um. Unsere Trainer (Andreas Ebbert-Karroum, Lars Rückemann und Michael Lex) unterstützen dabei zusätzlich in den Rollen Scrum Master und Product Owner. On top werden täglich kleine Theoriesessions angeboten, die weitergehende Konzepte (z.B. zum  Release Management, Build Management, TDD, ATDD, Testautomation und Codequalität) vermitteln, die dann in den folgenden Sprints praktisch erprobt werden können.

Fühlten sich die ersten Sprints noch etwas sperrig an, haben alle Teams mittlerweile Geschwindigkeit aufgenommen – die Velocity steigt! Zum Ende des Praktikums treffen wir uns alle in den Räumlichkeiten der codecentric in Düsseldorf. Dort stellt jedes Team in einer kleinen Präsentation seine Praktikumsergebnisse vor und bei Pizza & Bier klingt die Veranstaltung aus.

Gewinnen tun alle. Die Studenten erhalten die Möglichkeit, Softwareentwicklung state-of-the-art kennen zu lernen, Praxis KnowHow aufzubauen und ihre Teamskills zu verbessern. Die FH erfährt eine Aufwertung des Studiengangs und ein Alleinstellungsmerkmal im Wettbewerb mit anderen Hochschulen und für codecentric ist dies eine ideale Ergänzung zum Hochschulmarketing.

Wer als Teilnehmer eines offiziellen Trainings die ganze Welt der agilen SW-Entwicklung in einem Workshop erleben möchte, dem sei unser Agile Code Camp 2013 vom 24. bis 28. Juni 2013 empfohlen (http://www.codecentric.de).

CC-Grafik_AGILE_V2 ABC010 ABC015 ABC022 ABC030 ABC035 ABC040 ABC050 ABC055 ABC057 ABC058 ABC060 ABC062 ABC065 ABC070 ABC075 ABC080 ABC085 ABC090 ABC092 ABC095 ABC100 ABC102 ABC104 ABC106 ABC107 ABC108 ABC109 ABC110 ABC120 ABC990 ABC999

Standup- oder Sofameeting

$
0
0

Ob ein Meeting erfolgreich verläuft, hängt von vielen Faktoren ab, das richtige Format und der richtige Ort spielen dabei eine gewichtige Rolle.

Anlass für meine Überlegungen war ein Meeting, das im Kontext von mehreren zusammenarbeitenden Teams (Scrum of Scrum) stattfand. Ziel des Meetings war es, die Ergebnisse der einzelnen Teamretrospektiven abzugleichen und teamübergreifende Maßnahmen abzustimmen. Die Teams hatten in ihren Retrospektiven die anzusprechenden Maßnahmen identifiziert und ein Mitglied in das Abstimmungsmeeting delegiert. Die Delegierten haben sich in einer Ecke des offenen Arbeitsbereichs getroffen, und dort auf mehreren beweglichen Sofas  Platz genommen.

Als unbeteiligter Beobachter (Chicken) habe ich dieses Abstimmungsmeeting als träge und ineffizient empfunden. Die Teilnehmer haben dem Sprechenden nur mit halbem Ohr zugehört, die Sprechenden schweiften ab und der Moderator konnte die geplante Struktur des Meetings nicht halten. Als Ergebnis wurde die Timebox überschritten, ohne das am Ende das geplante Ziel erreicht wurde. Ich glaube, dass die gewählte Form und der Ort des Meetings erheblich dazu beigetragen haben.

Besprechnungsraum, Offene Teamfläche oder gar Arbeitsplatz

Die meist genutzten Orte für Meetings sind Besprechungsräume. Dabei handelt es sich um dediziert vorgesehene, abgeschlossene Räume, meist mit für Besprechungen hilfreicher Ausstattung wie feste Beamer, Whiteboards oder Flipcharts.

Den größten Vorteil dieser Räume stellt die gute Abschirmung gegen Störungen von außen dar. Die Teilnehmer können ihre Aufmerksamkeit vollständig auf das Thema und das Meeting richten. Für Außenstehende findet so ein Meeting im stillen Kämmerlein statt. Weitere Nachteile sind eher „logistischer“ Natur. Solche Räume sind meist nicht in ausreichendem Maße vorhanden und müssen im Voraus reserviert werden. Spontane Meetings sind oft nicht möglich. Auch liegt ein verfügbarer Raum nicht immer in der Nähe der normalen Arbeitsplätze der Teilnehmer, so dass eine längere „Anreise“-Zeit benötigt wird.

Alternativen zu dedizierten Besprechungsräumen stellen ruhigere Bereiche auf offenen Flächen dar. Dabei kann es sich z. B. um einen extra dafür vorgesehenen Bereich einer Teamarbeitsfläche, die größere Kaffeeküche oder eine Ecke in der Kantine (außerhalb der Mittagzeit) handeln. Die logistische Ausstattung variiert, je nach Ort, meist sehr stark.

Ein solcher Ort erhöht die Transparenz für Außenstehende, da jeder, der vorbei kommt, zumindest sieht, dass ein Meeting stattfindet und wer daran teilnimmt. Auch eine spontane Teilnahme ist durch Dazugesellen, Zuhören und sogar Mitreden möglich. Vorzeitiges Verlassen und späteres Hinzustoßen kann ohne größere Störung erfolgen. Dies ist aber auch einer der gravierenden Nachteile. Teilnehmer werden sehr einfach von außen gestört und abgelenkt und verlieren so den Fokus auf das Meeting.

Ein sehr spezieller Ort ist der Arbeitsplatz eines oder mehrerer Teilnehmer. Logistisch hat es für die Teilnehmer durch den kurzen Weg einen Vorteil. Für die Teilnehmer ist es aber viel schwerer, sich auf das Meeting zu fokussieren, da der eigene Arbeitsplatz eine Quelle vieler Ablenkungen darstellt (Telefon, Emails, Instant-Messenger).

Meetingformate

Das Format des Meetings ist im Vergleich zum Ort mit viel weniger Restriktionen wählbar.

Die oben beschrieben Situation, bei der die Teilnehmer im Kreis auf Sofas sitzen, halte ich für ein extremeres Beispiel. Das Format erzeugt eine entspannte und lockere Atmosphäre. Was aber auch schnell zu einer trägen Beteiligung am Meeting führt. So muss ein Teilnehmer, der aktiv das Flipchart nutzen möchte, aus den tiefen Sofas aufstehen. Da ein Moderator meist steht, wird auf der “Ebene” der Sitzenden leicht eine Nebendiskussion begonnen. Aufgrund des bequemen Sitzens dauern die Diskussionen länger.

Das absolute Gegenteil stellen dazu Stand-Up Meetings dar, in die Teilnehmer das Meeting im Kreis stehend durchführen. Dieses Format fördert meist Aufmerksamkeit für den aktuell Sprechenden und das Meeting insgesamt, da sich die Teilnehmer auf natürlich Weise dem Sprechenden zuwenden. Die Moderation erfolgt auf gleicher Augenhöhe und Diskussionen werden selten in die Länge gezogen. Auch eine aktive Beteiligung, zum Beispiel an einem Whiteboard, erfolgt mit einer sehr niedrigen Hemmschwelle durch einen einfachen Schritt in Richtung des Boards.

Fazit

Ort und Format des Meetings haben Einfluss auf Ablauf und Wirkung eines Meetings. Dessen sollte ich mir als Organisator/Moderator bewusst sein und es mir, falls möglich, für die jeweilige Situation zu nutze machen oder, falls nicht die optimale Wahl verfügbar ist, die negativen Einflüsse durch anderen Mittel kompensieren.

Der Softwerker

$
0
0

Bin sehr stolz heute die erste Ausgabe unseres Magazins “Der Softwerker” zu präsentieren.

Der Name ist dabei Programm. Software Craftsmanship ist unsere Leidenschaft bei codecentric und im Softwerker schreiben unsere Experten über Themen rund um Agile Softwareentwicklung, Continuous Delivery und moderne Technologien.

In dieser Ausgabe haben wir folgende Artikel für Euch aufbereitet:

  • Software Craftsmanship ohne Craftsmanship? (Uwe Friedrichsen)
  • Die DevOps-Bewegung (Patrick Peschlow)
  • Der Big Data Developer (Pavlo Baron)
  • Spring komplett ohne XML – geht das? (Tobias Flohre)
  • Das Problem mit der Architekturqualität (Uwe Friedrichsen)
  • Erfolgreiche Festpreisprojekte durch flexiblen Inhalt (Mirko Novakovic)
  • Continuous Delivery in der Praxis (Marcel Birkner)

Momentan gibt es die Ausgabe kostenlos nur in gedruckter Form, weil wir ein hochwertiges Magazin Format für den “Softwerker” gewählt haben. Registriert Euch einfach kostenlos auf http://www.codecentric.de/softwerker/ und wir schicken Euch jede Ausgabe zu.

softwerker

Bin gespannt auf Euer Feedback und Vorschläge für die nächsten Ausgaben.

Retrospektiven – sind sie nützlich?

$
0
0

Sind Retrospektiven nützlich? Oder sind sie nur Zeitverschwendung?
Können Sie gefährlich sein? Wann sind sie nützlich?
Dienen sie wirklich der Prozessverbesserung?

Retrospektiven (insbesondere agile Retrospektiven) sind mittlerweile bekannte Praxis.
Zugleich berichten Teams häufig, dass sie gerade diese Praxis nach anfänglichem Gebrauch aufgegeben haben.
Retrospektiven werden allgemein als Mittel zur Prozessverbesserung angepriesen, wobei ich, in meiner Praxis, selten erlebt habe, dass sie tatsächlich zu diesem Zweck eingesetzt werden.

Ich finde Retrospektiven sehr nützlich, wenn sie ordnungsgemäß ausgeführt werden. Gleichzeitig habe ich Situationen erlebt (und in einigen Fällen bin ich auch schuldig), wenn Retrospektiven demotivierend und geradezu gefährlich für das Team sein können.

Patrick Kua hält Retrospektiven für sehr nützlich, wenn sie in einer feindlichen Umgebung für ein nicht funktionierendes Team ausgeführt werden.

In meiner Erfahrung habe ich Retrospektiven als wertvolle Methode zu schätzen gelernt, ein Team durch die klassischen Phasen der Teamentwicklung (Forming – Storming – Norming-…) zu begleiten. Für Teams, in denen ich Mitglied war, waren Retrospektiven nützlich:

  • um gemeinsame Regeln zu etablieren,
  • um Vertrauen aufzubauen und Bedenken zu äußern,
  • um über den Tellerrand zu schauen und zu verstehen, warum die Dinge so sind, wie sie sind,
  • um zu bestimmen, was getan werden muss und sich zu verpflichten, dies zu tun,
  • um Risiken zu erkennen und zwischenmenschliche Probleme frühzeitig zu klären.

Die Resultate der Retrospektiven waren besseres Teamklima, eine sichere Umgebung, in der man Probleme melden und lösen kann, ein besseres Vertrauensverhältnis zum Kunden, Fortschritt bei der Lösung von Problemen und allgemein ein Gefühl von einem besser funktionierenden Team.

Ist das nützlich? Lohnt es sich?
Meiner Meinung nach definitiv: ja!

Gibt es Situationen, in denen Retrospektiven gefährlich sind?
Ebenso definitiv: ja!

Wenn Retrospektiven sich nur um das drehen, was nicht funktioniert, ohne in Verbesserungsvorschlägen und konkrete Aktionen zu resultieren, kann das Team auf jeden Falls schnell demotiviert werden.

Wollen Sie mehr über die Themen erfahren, die hier angerissen wurden? Über Ihre Erfahrungen mit Retrospektiven plaudern? Fragen stellen oder etwas Neues über Retrospektiven oder Agilität im Allgemeinen erfahren?
Dann treffen Sie uns beim 1. Agiler-Stammtisch in Frankfurt, dessen Hauptthema, natürlich, “Retrospektiven” sein wird.
Er findet am 13. Juni im Coworking Space “Die Zentrale”, Berger Str. 175, Frankfurt (U-Bahn: Bornheim Mitte) statt.

Wir freuen uns auf Ihren Besuch!


MongoDB für den Roboter

$
0
0

Wir setzen das Robot Framework seit geraumer Zeit für automatisierte Softwaretests in unseren Projekten ein. Außerdem beschäftigen sich ein paar meiner Kollegen mit der NoSql Datenbank MongoDB (Tutorial über MongoDB). Die Dokumenten-Management-Lösung Center-Device (Start-Up-Unternehmen der Codecentric AG) setzt ebenfalls im Back-End auf MongoDB (Center-Device Architektur-Überblick). Da dachte ich mir: Es ist Zeit für eine neue Robot-Library: robotframework-mongodblibrary. Die Version 0.2.1 steht als download auf GitHub bereit (Keyword-Dokumentation).

Die Bibliothek ist in Java (Jython) geschrieben und verwendet intern den MongoDB Java Treiber. Das Programmieren der Library war trotz geringer Vorerfahrung dank der guten MongoDB-Dokumentation sehr einfach :-)

Der erste Robot-Test

*** Settings ***
Library de.codecentric.robot.mongodblibrary.keywords.MongodbLibrary
Test Setup Setup MongoDB
 
*** Test Cases ***
should insert given document
     Insert Document myCollection {say : 'Hello MongoDb!'}
     Collection Should Exist myCollection
     Document Should Exist myCollection {say : 'Hello MongoDb!'}
 
*** Keywords ***
Setup MongoDB
     Connect To Server localhost 27020 robotdb1
     Drop Database robotdb1

Robot Remote Library

Auch die Robot-Remote-Bibliothek ist schon mit an Board :-) Dann kann man die Bibliothek auch als eigenen Server starten. Sehr nüzlich, wenn man beispielsweise den Haupt-Test mit Python anstatt Jython starten möchte.

*** Settings ***
Library   Remote    http://localhost:8270
Test Setup Setup MongoDB
 
*** Test Cases ***
should insert given document
     Insert Document myCollection {say : 'Hello MongoDb!'}
     Collection Should Exist myCollection
     Document Should Exist myCollection {say : 'Hello MongoDb!'}
 
*** Keywords ***
Setup MongoDB
     Connect To Server localhost 27020 robotdb1
     Drop Database robotdb1

vor der Testausführung muß natürlich noch der Server gestartet werden:

java -jar build/libs/robotframework-mongodblibrary-0.2.1-with-dependencies.jar --port 8270

MongoDB-Server Embedded

Ein neues Feature seit der Version 0.2 ist das Starten eines MongoDB-Servers (in einer bestimmten Version) innerhalb der Tests, hierfür wird die Embedded MongoDB Bibliothek verwendet. Das Keyword lädt den Server von der MongoDB-Seite herunter, entpackt diesen ins Home-Verzeichnis und startet anschließend den MongoDB-Daemon in einem eigenen Betriebssystem-Prozess (MongoDB ist in C++ geschrieben). Beim Beenden der Test-Suite sollte das Keyword Shutdown Embedded verwendet werden, um die Datenbank sauber herunterzufahren.

*** Settings ***
Library de.codecentric.robot.mongodblibrary.keywords.MongodbLibrary
Suite Setup  Startup Embedded  2.4.4
Suite TearDown  Shutdown Embedded
Test Setup Setup MongoDB
 
*** Test Cases ***
should insert given document
     Insert Document myCollection {say : 'Hello MongoDb!'}
     Collection Should Exist myCollection
     Document Should Exist myCollection {say : 'Hello MongoDb!'}
 
*** Keywords ***
Setup MongoDB
     Connect To Server localhost 27020 robotdb1
     Drop Database robotdb1

Der Hauptvorteil der Server-Integration liegt in der Übersichtlichkeit der Robot-Logdateien, die MongoDB-Logmeldungen werden direkt ins Robot-Log geschrieben und können so dem jeweiligen Keyword zugeordnet werden:

embeddedServer

Verwendung in anderen Test-Frameworks

Die Bibliothek hat keinerlei Abhängigkeiten zum Robot-Framework und kann deswegen auch in anderen Test-Frameworks (z.B. Junit, JBehave) verwendet werden. Hierfür gibt es eine Download-Version ohne Abhängigkeiten.


Viel Spaß mit der Bibliothek :-)

Was ist eigentlich Lean Coffee?

$
0
0

Stellen Sie sich zum Anfang diese Fragen:

  • Haben Sie schon mal ein User Group Treffen besucht, bei dem viele der Themen für sie gar nicht von Interesse und Bedeutung waren?
  • Haben Sie bereits an Diskussionen teilgenommen, welche durch eine oder zwei Personen dominiert wurden?
  • Kennen Sie das Gefühl, wenn Diskussion wesentlich mehr Zeit brauchen als Sie eigentlich gedacht haben?

Wenn ihre Antwort auf eine dieser Fragen „Ja“ lautet, dann sind Treffen im Lean-Coffee Format möglicherweise für Sie die richtige Lösung für das altbekannte Problem “Wie erreiche ich so viel wie möglich in begrenzte Zeit und diskutiere dabei über das, was für mich wichtig ist?”

Allgemein gehören Treffen im Lean-Coffee Format zu den Treffen ohne Agenda, bei denen die Teilnehmer die Tagesordnung erst zusammen am Anfang des Treffens definieren.

Die Ideen dahinter sind dabei sehr simpel:

  • Die Themen sollen von den Teilnehmern kommen
  • Die Themen sollen für die Teilnehmer relevant sein
  • Eine unterstützende Moderation sorgt für Führung

Kommt Ihnen das eventuell bekannt vor und erinnert sie an Ihr letztes Open Space Treffen? Dann haben Sie vollkommen Recht! Doch bei aller Gleichheit gibt es einige Unterschiede:

  • Es gibt keine parallel Themendiskussion während des Lean-Coffees
  • Diskussionen der Themen sind durch Timeboxing kontrolliert
  • Am Ende des Treffens muss kein Aktionsplan stehen und daher ist Lean-Coffee besser zum Erfahrungsaustausch geeignet

Ok, genug mit der Theorie! Wollen Sie einen Lean-Coffee ausprobieren? Haben Sie Interesse an agilen Arbeitsweisen? Wollen Sie Ihre Erfahrungen mit anderen Experten in freundlicher Umgebung umtauschen?

Wenn das der Fall ist, dann würden wir uns freuen, Sie auf Agilen Stammtisch in Frankfurt zu sehen. Wir treffen uns immer einmal pro Monat (ab August – jeden 1. Donnerstag des Monats). Das nächste Treffen wird am 11. Juli um 19:00 in Die Zentrale, Berger Strasse 175, 60385 Frankfurt stattfinden. Mehr Detail gibt es auf unserer XING-Seite.

Ein Hinweis zum Schluss: Natürlich sind Sie sind nicht verpflichtet nur Kaffee während dieses Treffens zu trinken. Altbier, Pils, Kölsch, Äppelwoi, Cider und auch Schorle sind genauso in Ordnung :-)

The post Was ist eigentlich Lean Coffee? appeared first on codecentric Blog.

Cucumber: Web-Anwendungen mit Capybara, Poltergeist und PhantomJS testen

$
0
0

Dieses Tutorial zeigt, wie sich Akzeptanztests für Web-Anwendungen mit Cucumber, Capybara, Poltergeist und PhantomJS schreiben lassen. Unterwegs werden wir auch einige andere interessante Technologien wie Node.js and AngularJS streifen. Dies ist der zweite Teil einer Mini-Serie zu Cucumber – wem Cucumber bisher noch nicht bekannt ist, kann auch mit dem ersten Teil einsteigen, der das Setup und die Grundlagen von Cucumber behandelt.

Der Tool-Stack im Detail – Capybara, Poltergeist und PhantomJS

Bevor wir unseren ersten Test schreiben, sollten wir uns einen Moment Zeit nehmen, um die Werkzeuge zu betrachten, die wir zusätzlich zu Cucumber noch einsetzen werden:

  • Capybara nennt sich selbst ein “Akzeptanztest-Framework für Web-Anwendungen”. Moment mal – ist Cucumber nicht schon ein Akzeptanztest-Framework? Warum benötigen wir noch ein weiteres in unserem Stack? Zuerst einmal ist Cucumber ein Framework für Behaviour Driven Development und nicht per se ein Akzeptanztest-Framework. Man könnte Cucumber für Unit Tests einsetzen, für Akzeptanztests oder für irgendetwas dazwischen. Des Weiteren hat Cucumber nichts mit Web-Anwendungen am Hut. Cucumber gibt uns nur die Möglichkeit, unsere Tests (oder Specs oder Szenarios, wie immer man sie auch nennen möchte) in einer sehr lesbaren, nicht-technischen Form (Gherkin) zu schreiben und auszuführen. Capybara hingegen kümmert sich nun um den Aspekt “Web-Anwendung” und versteckt die Details, die notwendig sind, um einen Browser fernzusteuern (via Selenium oder Poltergeist) hinter einer schicken API. Des Weiteren lässt sich Capybara gut mit Cucumber kombinieren (siehe Abschnitt Using Capybara with Cucumber in der Capybara-Dokumentation). Es ist genau der richtige Abstraktionslevel, um Tests für Web-Anwendungen zu schreiben. Natürlich könnte man auch in den Cucumber Step Definitions so etwas wie Selenium direkt verwenden, aber meiner Meinung nach ist das zu low-level und erfordert deutlich mehr Code für das gleiche Ergebnis.
  • Poltergeist ist ein PhantomJS Treiber für Capybara. Es stellt die Verbindung zwischen Capybara und PhantomJS dar. Nachdem Poltergeist einmal als Treiber in Capybara konfiguriert ist, interagiert man nicht mehr direkt mit Poltergeist oder PhantomJS, sondern nur noch mit der Capybara API.
  • PhantomJS ist ein vollständiger Browser, der allerdings headless (also ohne graphische Anzeige) läuft. Es basiert auf WebKit und unterstützt alle wichtigen Web-Standards (CSS, JavaScript, …). Es eignet sich ideal für automatisierte Tests von Web-Anwendungen, da es ausgesprochen einfach ist, es auf einem CI-Server laufen zu lassen (selbst, wenn auf diesem kein X installiert ist).

Setup

Einrichten der zu testenden Anwendung

Da dieses Tutorial sich mit dem Testen von Web-Anwendungen beschäftigt, benötigen wir offensichtlich eine solche. Wer öfter Beiträge im codecentric-Blog liest, dem ist vielleicht schon einmal die “Movie Database” in einer Ihrer verschiedenen Inkarnationen begegnet. Da ich mich kaum für Filme interessiere, dafür aber umso mehr für Hörbücher, wird unsere Beispielanwendung diesmal der “Audiobook Collection Manager” sein, eine sehr einfache, auf AngularJS basierende CRUD-Anwendung, mit der man seine Hörbuch-Sammlung verwalten kann. Unter https://github.com/basti1302/audiobook-collection-manager-ui findet sich eine kurze Anleitung, um die Anwendung einzurichten. Im Verlauf der Installation muss auch Storra per git geklont werden, ein simpler, in Node.js geschriebender REST-Service, der vom Audiobook Collection Manager Frontend zur Speicherung der Daten benutzt wird. Kleiner Hinweis am Rande: Im ersten Teil des Tutorials hatte ich erwähnt, dass Cucumber mittlerweile auf viele andere Sprachen (außer Ruby) portiert wurde. Wer einen Blick auf cucumber.js, den JavaScript-Port von Cucumber, werfen möchte, sollte einmal in das Verzeichnis storra/features schauen.

Wenn der Audiobook Collection Manager eingerichtet ist und läuft, kann man kurz manuell ein wenig in der Anwendung umherklicken, um zu sehen, was man damit machen kann (nicht viel – es ist ja auch nur eine Beispielanwendung).

Capybara und Poltergeist einrichten

Wer den ersten Teil des Tutorials verfolgt hat, sollte das Beispiel-Projekt bereits ausgecheckt haben. In diesem Fall muss

git checkout 03_setup_capybara

ausgeführt werden, um den neuen Gemfile mit den Gems für Capybara und Poltergeist zu bekommen. (In diesem Branch wurde das Cucumber Feature und die dazugehörige Step-Datei aus dem ersten Teil des Tutorials entfernt, da wir es nicht mehr benötigen.)

Wenn das Repository noch nicht ausgecheckt ist, sollte das jetzt nachgeholt werden.

git clone -b 03_setup_capybara https://github.com/basti1302/audiobook-collection-manager-acceptance.git

Da sich das Gemfile geändert hat, muss nun als erstes bundle install ausgeführt werden. Damit werden die Capybara- und Poltergeist-Gems und deren Abhängigkeiten installiert. Anzumerken ist hier, dass momentan der Poltergeist-Gem nicht aus einem Gem-Repository kommt, sondern direkt aus einem Github-Repository; ein Fork des Originals der einen Bugfix für Windows enhält, der noch nicht gemergt wurde. Dass man als Quelle für einen Gem ein Git-Repository direkt referenzieren kann, ist ein nettes Feature des Bundler-Gems, es macht es allerdings erforderlich, Cucumber nun auf jeden Fall mit bundle exec cucumber zu starten, statt einfach nur mit cucumber.

Im ersten Teil des Tutorials wurde auch PhantomJS installiert. Wer diesen Schritt ausgelassen hat, kann das jetzt nachholen –
http://phantomjs.org/download.html.

Der Branch von audiobook-collection-manager-acceptance, den wir gerade ausgecheckt haben, beinhaltet bereits den erforderlichen Code zu Konfiguration von Capybara und Poltergeist. An dieser Stelle ist also nichts mehr zu tun. Wir können uns den Konfigurations-Code kurz anschauen, er ist aufgeteilt auf features/support/env.rb und features/support/hooks.rb. Die Datei env.rb beginnt mit einigen require-Anweisungen:

require 'rspec/expectations'
require 'capybara/cucumber'
require 'capybara/poltergeist'

Die Anweisung require 'rspec/expectations' habe wir bereits im ersten Teil des Tutorials diskutiert – sie sorgt dafür, dass die RSpec Object Expectations in allen Step-Files verfügbar sind. require 'capybara/cucumber' bewirkt das gleiche für die Methoden der Capybara API. require 'capybara/poltergeist' ist erforderlich, um Poltergeist als Browser-Treiber für Capybara zu registrieren.

Nach dem require-Block kommt ein etwas längliches if-else-Konstrukt:

if ENV['IN_BROWSER']
  # On demand: non-headless tests via Selenium/WebDriver
  # To run the scenarios in browser (default: Firefox), use the following command line:
  # IN_BROWSER=true bundle exec cucumber
  # or (to have a pause of 1 second between each step):
  # IN_BROWSER=true PAUSE=1 bundle exec cucumber
  Capybara.default_driver = :selenium
  AfterStep do
    sleep (ENV['PAUSE'] || 0).to_i
  end
else
  # DEFAULT: headless tests with poltergeist/PhantomJS
  Capybara.register_driver :poltergeist do |app|
    Capybara::Poltergeist::Driver.new(
      app,
      window_size: [1280, 1024]#,
      #debug:       true
    )
  end
  Capybara.default_driver    = :poltergeist
  Capybara.javascript_driver = :poltergeist
end

Es nicht notwendig, das im Detail zu analysieren, der springende Punkt ist: Im Standardfall benutzen wir den else-Zweig. Dort wird Poltergeist als Treiber für Capybara registriert, Poltergeist wiederum benutzt dann PhantomJS. Da PhantomJS headless läuft, ist es sehr einfach in eine Continuous-Integration-Umgebung zu integrieren.

Wenn man sich das Ganze allerdings ab und an in einem richtigen Browser anschauen will, kann man die Tests folgendermaßen starten:

IN_BROWSER=true bundle exec cucumber

oder unter Windows:

SET IN_BROWSER=true
bundle exec cucumber

Wenn diese Umgebungsvariable vorhanden ist, wird Selenium WebDriver statt Poltergeist und Firefox statt PhantomJS benutzt, so dass man sich die Szenarien live und in Farbe anschauen kann. Wem das zu schnell geht, kann sie mit

IN_BROWSER=true PAUSE=1 bundle exec cucumber

oder unter Windows

SET IN_BROWSER=true
SET PAUSE=1
bundle exec cucumber

starten, so dass Cucumber nach jedem Step eine Sekunde wartet.

Der Rest von env.rb definiert einige Konstanten sowie Hilfsmethoden, um auf die getestete Anwendung zuzugreifen.

hooks.rb enhält momentan nur Folgendes:

After do |scenario|
  if scenario.failed?
    save_page
  end
end

Dies registriert einen sogenannten Hook, der nach jedem Szenario aufgerufen wird. Wenn das Szenario fehlgeschlagen ist, schreibt Capybara eine Momentaufnahme des HTMLs der aktuellen Seite in eine Datei. Cucumber documentation enthält weitergehende Informationen zu Hooks.

Testen der Web-Anwendung

Das erste Feature mit Capybara

Nachdem nun das Setup und die Konfiguration erledigt ist, können wir mit unserem ersten Szenario loslegen.

Die erste Funktion, die wir testen werden, ist das Auflisten aller Hörbücher in der Sammlung. Ohne weitere Umschweife schreiben wir einfach auf, welches Verhalten wir von der Anwendung erwarten, wenn der Benutzer auf die Seite geht, die die Liste aller Einträge darstellt. Die Datei features/list_audiobooks.feature könnte folgendermaßen aussehen:

Feature: Display the list of audio books
  In order to know which audio books the collection contains
  As an audio book enthusiast
  I want to see a list of all audio books
 
  Scenario: Display the list of all audio books in the collection
    Given some audio books in the collection
    When I visit the list of audio books
    Then I see all audio books

Anmerkung: Sämtlicher Code der in diesem Abschnitt zu sehen ist (das Cucumber-Feature, der Step-File und die Datei storage.rb) ist im Branch 04_list_audiobooks verfügbar. Mit git checkout 04_list_audiobooks kann man sich den Code holen.

Zur Erinnerung: In einem Szenario kann man im Wesentlichen schreiben, was man möchte, solange jede Zeile mit Given, When, Then oder And beginnt. Man muss (und sollte) an dieser Stelle noch nicht darüber nachdenken, wie man die einzelnen Schritte später implementiert. Stattdessen kann man sich ganz darauf konzentrieren, wie man die aktuellen Anforderung formuliert. Aus diesem Grund ist es oft günstiger, das Szenario zuerst zu schreiben und die Step Definitions erst im Anschluss. Das obige Szenario sollte weitestgehend selbsterklärend sein.

Wir können bei Bedarf nun wieder bundle exec cucmber ausführen, um von Cucumber Vorschläge für die Step Definitions zu bekommen. Die Step Definitions kommen diesmal in die Datei features/step_definitions/audiobooks.rb:

#encoding: utf-8
 
Given /^some audio books in the collection$/ do
  upload_fixtures backend_url('audiobooks'), $fixtures
end
 
When /^I visit the list of audio books$/ do
  visit ui_url '/index.html'
end
 
Then /^I see all audio books$/ do
  page.should have_content 'Coraline'
  page.should have_content 'Man In The Dark'
  page.should have_content 'Siddhartha'
end

Diese Step Definitions benutzen die Capybara DSL, um die Schritte des Szenarios zu implementieren – mehr Informationen zur Capybara DSL findet man auf der Capybara Github-Seite oder auf rubydoc.info.

Der “Given” Step ruft upload_fixtures auf, eine Methode die in features/support/storage.rb implementiert ist und Storra (der Persistenz-Service der von der AngularJS UI benutzt wird) direkt anspricht, um einige Hörbücher in die Datenbank zu schreiben. Der Code hat nicht mit Cucumber oder Capybara zu tun, daher gehe ich an dieser Stelle nicht weiter darauf ein.

Der “When” Step: Klammern sind in Ruby bei Methodenaufrufen optional, daher ist visit ui_url '/index.html' äquivalent zu visit(ui_url('/index.html')) (meiner Meinung ist die Version ohne Klammern flüssiger lesbar, aber das ist natürlich Geschmackssache). ui_url ist eine Hilfsmethode aus env.rb die einen Pfad in eine vollständige URL in der zu testenden Anwendung übersetzt. visit schließlich ist eine Methode von Capybara, die zu der gegebenen URL navigiert. Insgesamt veranlasst visit ui_url '/index.html' also den Browser (PhantomJS in diesem Fall) http://localhost:8000/app/index.html abzurufen, die Seite, die die Hörbücher auflistet.

Der “Then” Step benutzt Capybaras page-Objekt (welches die aktuelle Seite repräsentiert, also den DOM, der momentan im Browser vorliegt) um zu verifizieren, dass drei bestimmte Textfragmente vorhanden sind. Nehmen wir einmal an, dass der Step Given some audio books in the collection drei Hörbücher hinzufügt, und zwar “Coraline” von Neil Gaiman, “Man in the Dark” von Paul Auster und “Siddhartha” von Herman Hesse. Wenn man sich die Dokumentation von Capybara ansieht, würde man nun erwarten, dass die Überprüfung in etwa so erfolgt: page.has_content?('Coraline'). Um das ganze etwas interessanter zu machen, benutzen wir hier die Fähigkeit von RSpec, zu jedem Prädikat (eine Methode, deren Name mit “?” endet) bei Bedarf zur Laufzeit einen Custom Matcher zu generieren. Siehe dazu auch die RSpec Matcher Doku. Die RSpec-Bibliothek nutzt hierzu Meta-Programming in Ruby und daher können wir page.should(have_content('Coraline')) schreiben oder eben ohne Klammern page.should have_content 'Coraline'.

Ein Wort zu Test-Daten

Wenn man das Feature mehrfach ausführt und dann http://localhost:8000/app/index.html im Browser öffnet, wird man feststellen, dass die Liste ziemlich gewachsen ist, sie enthält jetzt jeweils mehrere Exemplare der Hörbücher, die im “Given” Step hinzugefügt wurden. Das kann zu einem echten Problem werden, insbesondere, wenn wir später Funktionen wir das Hinzufügen oder Löschen von Hörbüchern testen wollen. Die Tests sind nicht mehr voneinander isoliert, da sie auf derselben Datenbank operieren. Beispiel: Ein Szenario für das Hinzufügen eines neuen Hörbuchs, dass beispielsweise den Titel “Foobar” in die Sammlung einfügt. Um zu testen, ob das Hinzufügen erfolgreich war, würden wir vermutlich page.should have_content 'Foobar' in einem “Then” Step ausführen, nachdem wir zur Liste aller Hörbücher zurücknavigiert haben. Nun nehmen wir an, dass der Test einige Male erfolgreich durchläuft. Aber was, wenn später die Funktion zum Hinzufügen im produktiven Code kaputt geht? Unser Cucumber-Szenario wäre unter Umständen immer noch grün, da bereits mehrere Exemplare von “Foobar” in der Datenbank sind und damit in der Liste auftauchen. Das ist der Worst Case für einen automatisierten Test: Der Test ist grün obwohl der produktive Code kaputt ist. Also müssen wir uns darum kümmen.

Dieses Problem kann bei jeder Anwendung bei Akzeptanztests auftreten und es gibt mindestens zwei Lösungen dafür:

  1. Vor oder nach jedem Test wird die komplette Datenbank gelöscht. Das ist oft die einfachere Option. Diese Strategie macht es natürlich erforderlich, im CI einen gesondertes Umgebung für die Akzeptanztests bereitzustellen – zusätzlich zu einer Stage für manuelle Tests. Dies sollte allerdings jedes nicht triviale Projekt ohnehin haben.
  2. Die Tests werden dadurch isoliert, dass jeder Test einen separaten “Namensraum” benutzt. Hier hängt es vom Domänenmodell ab, ob diese Strategie genutzt werden kann. Kurzes Beispiel: Angenommen, wir haben ein Kunden Objekt, dem ein oder mehrere Bestellungen zugeordnet sind, denen wiederum Artikel zugeordnet sind. Ein Kunde interagiert immer nur mit seinen eigenen Bestellungen und sieht auch nur diese. In einem solchen Fall könnte jeder Test im ersten Schritt einen neuen Kunden anlegen (mit einer neuen, eindeutigen ID) um so die Tests voneinander zu isolieren.

Wir verwenden die erste Strategie und löschen vor jedem Test per Storra die gesamte Hörbuch-Sammlung. Dazu wird Folgendes zu features/support/hooks.rb hinzugefügt:

Before do
  delete_database backend_url('audiobooks')
end

Und die Methode delete_database wird in features/support/storage.rb implementiert:

def delete_database(url)
  RestClient.delete url
end

Hinweis: backend_url ist in features/support/env.rb definiert und gibt die URL zurück, unter der die Collection 'audiobooks' von Storra verwaltet wird.

Weitere Tests für die Liste der Hörbücher

Hinweis: Der Code des folgenden Szenarios ist per git checkout 05_filter verfügbar. Dieser Branch enthält auch den im vorigen Abschnitt besprochenen Hook zum Löschen der Datenbank.

Wenn man sich im Audio Book Collection Manager etwas umschaut, fällt einem evtl. das Texteingabefeld mit dem Label “Filter” auf. Wenn man anfängt, in diesem Feld etwas zu tippen, werden nur noch Hörbücher angezeigt, die zu dem Suchbegriff passen. Tippt man also z. B. “Cor”, wird das Hörbuch “Coraline” angezeigt, alle anderen Titel nicht.

Um dies in einem Cucumber-Szenario auszudrücken, können wir den folgenden Code zu features/list_audiobooks.feature hinzufügen:

  Scenario: Filter the list
    Given some audiobooks in the collection
    When I visit the list of audiobooks
    And I search for "Cor"
    Then I only see titles matching the search term
    When I remove the filter
    Then I see all audiobooks again

Um die Steps zu implementieren, fügen wir Folgendes zu features/step_definitions/audiobooks.rb hinzu:

When /^I search for "(.*?)"$/ do |search_term|
  fill_in('filter', :with =&gt; search_term)
  @matching_titles = ['Coraline']
  @not_matching_titles = ['Man In The Dark', 'Siddharta']
end
 
When /^I remove the filter$/ do
  # funny, '' (empty string) does not work?
  fill_in('filter', :with =&gt; ' ')
  @matching_titles = @not_matching_titles = nil
end
 
Then /^I see all audiobooks(?: again)?$/ do
  page.should have_content 'Coraline'
  page.should have_content 'Man In The Dark'
  page.should have_content 'Siddhartha'
end
 
Then /^I only see titles matching the search term$/ do
  @matching_titles.each do |title|
    page.should have_content title
  end
 
  @not_matching_titles.each do |title|
    page.should have_no_content title
  end
end

Betrachten wir die Step Definitions eine nach der anderen.

  • When I search for...: Die erste Zeile (fill_in('filter', :with => search_term)) benutzt Capybaras API um einen Wert in das Texteingabefeld einzutragen. Der Wert wird durch die Capturing Group des reguläre Ausdrucks festgelegt. Wird der Step also mit When I search for "Foobar" aufgerufen, würde “Foobar” in das Eingabefeld eingetragen. Die nächsten beiden Zeilen setzen die Instanzvariablen @matching_titles und @not_matching_titles. Hier werden also Erwartungen (oder besser: erwartete Werte) in einem When-Step definiert, damit diese später in einem Then-Step überprüft werden können. In diesem Fall werden die erwarteten Werte direkt im nächsten Schritt geprüft. Diese Vorgehensweise mag diskussionswürdig sein, da Erwartungen in einem When-Step eigentlich nichts zu suchen haben, dafür ist ja der Then-Step da. Allerdings erlaubt diese Technik oft auf pragmatische Art und Weise, einen Then-Step mit verschiedenen Erwartungen (die dann in verschiedenen When-Steps definiert werden) zu benutzen. In diesem speziellen Fall gibt es allerdings auf jeden Fall noch Verbesserungspotenzial, da nur der Text für fill_in variabel ist (die Variable search_term, aus der Capturing Group), die Werte für @matching_titles und @not_matching_titles jedoch fest sind.
  • Then I only see titles matching the search term: Dieser Schritt benutzt die erwarteten Werte, die im When-Step festgelegt wurden. Wir überprüfen, dass alle Hörbuch-Titel, die zum Filter passen, angezeigt werden. Ebenso überprüfen wir für die Titel, die nicht zum Filter passen, dass sie auch tatsächlich nicht angezeigt werden.
  • When I remove the filter: Dieser Schritt entfernt den eingegebenen Text wieder aus dem Filter-Feld.
  • Die Step-Definition Then /^I see all audiobooks(?: again)?$/ do ist unsere bereits existierende Step-Definition (Then /^I see all audiobooks/ do), nur dass der reguläre Ausdruck mit einer optionalen Non-Capturing Group (?: again)? erweitert wurde, damit sich das Szenario flüssiger liest. (Wenn der obige Code per Copy and Paste in die vorhandene audiobooks.rb eingefügt wurde, muss die alte Step-Definition ohne erweiterten regulären Ausdruck gelöscht werden, um mehrdeutige Steps zu vermeiden.)

AJAX-Funktionalität mit Capybara testen

Lehnen wir uns einen Moment zurück – was genau passiert in dem Test, den wir gerade implementiert haben? Die getestete Anwendung ist mit AngularJS implementiert, also eine Single Page Application. Es findet keine Navigation statt, die Seite wird nie neu geladen. Alles passiert auf einer Seite durch asynchrones JavaScript, DOM-Manipulation und asynchronen Datenaustausch mit dem Backend (Storra). Die Filter-Funktionalität, die wir im letzten Abschnitt getestet haben passiert zum Beispiel komplett client-seitig. Wenn sich der Filter ändert, werden einige Einträge aus dem DOM entfernt, andere werden ggf. wieder angezeigt.

Hat uns das das Testen erschwert? Waren die Steps dadurch komplizierter zu implementieren? Haben wir speziellen Code benötigt, um auf die asynchronen Teile zu warten? Nein! Wir haben uns bis jetzt eigentlich noch gar keinen Gedanken darum gemacht. Der Grund dafür ist folgender: Capybara nimmt einfach grundsätzlich an, dass in einer modernen Web-Anwendung potenziell alles asynchron sein kann. Immer, wenn man per Capybara überprüft, ob bestimmte Inhalte da sind oder eine gewisse Bedingung erfüllt ist, wartet Capybara darauf, dass der Inhalt erscheint oder die Bedingung erfüllt ist, anstatt es nur einmalig zu überprüfen.

Das ist genau das Verhalten, dass ich von einer guten Browser-Abstraktionsschicht heute erwarte: Wenn ich Akzeptanztests schreibe, will ich nicht darüber nachdenken müssen, ob die Anwendung den erwarteten Inhalt durch Navigation zu einer anderen Seite oder durch ein partielles DOM-Update erzeugt. Ich will nur testen, ob der Inhalt da ist. Des Weiteren will ich meinen Test nicht anpassen, wenn die Anwendung die Inhalte morgen auf andere Art und Weise erzeugt.

Capybara erfüllt diese Erwartungshaltung. Eine ganze Reihe andere Test-Frameworks für Web-Anwendungen sind weit davon entfernt, insbesondere die Frameworks, die auf einer niedrigeren Abstraktionsstufe operieren, wie zum Beispiel Selenium. Wenn der Test-Code mit Anweisungen der Art waitForXyz oder sogar dem haarsträubenden (weil willkürlichen) sleep 2s übersät ist, benutzt man vermutlich das falsche Test-Framework.

Weitere Cucumber-Features und Scenarios

Wir könnten nun noch eine ganze Menge weitere Features und Scenarios für den Audio Book Collection Manager schreiben. Der Branch master des Beispiel-Cucumber-Projekts enthält weitere Features, zum Beispiel für das Hinzufügen eines neuen Hörbuchs oder zum Anzeigen detaillierterer Informationen zu einem Eintrag. Mit git checkout master kann man sich diese anschauen. Wer das Tutorial bis hierhin durchgearbeitet hat, dem werden in den zusätzlichen Features und Step-Files wohl keine allzu großen Überraschungen mehr begegnen.

Interessanter wäre es ggf., auf eigene Faust ein paar Cucumber-Features zu schreiben. Wie wäre es mit einem Feature für das Löschen von Hörbüchern? Oder für die Anzeige des CD-Covers eines Hörbuchs? Die entsprechende Funktionalität ist in der Anwendung bereits implementiert, es fehlt nur noch der Cucumber-Test. (Das Cover-Bild wird in der Detail-Ansicht automatisch angezeigt, falls eine ASIN eingetragen ist und amazon.com ein Bild für diese ASIN liefert).

Fazit

Damit endet unser kleiner Ausflug in die Welt von Cucumber und Capybara. Dieses Tutorial zeigt natürlich nur einen kleinen Ausschnitt von dem, was möglich ist. Ich hoffe, es wurde trotzdem deutlich, dass diese Kombination recht mächtig ist und sich damit sehr elegante und lesbare Akzeptanztests schreiben lassen.

Credits

Ein Teil des Setup-Codes (insbesondere das Umschalten zwischen Poltergeist/PhantomJS und Selenium WebDriver/Firefox durch eine Umgebungsvariable) wurde von Ruby-Guru Michael Schuerig geschrieben. Durch Michael bin auch zum ersten Mal mit Capybara und Poltergeist in Berührung gekommen.

The post Cucumber: Web-Anwendungen mit Capybara, Poltergeist und PhantomJS testen appeared first on codecentric Blog.

Cucumber: Setup und Grundlagen

$
0
0

Es gibt viele gute Akzeptanz-Test-Werkzeuge und BDD-Frameworks – und hier im codecentric-Blog wurde auch schon über eine ganze Reihe davon geschrieben, z. B. über das Robot Framework, Fitnesse, JBehave, Geb und andere. Ein wichtiges und beliebtes Werkzeug in diesem Bereich wurde bisher allerdings sträflich vernachlässigt: Cucumber. Es ist an der Zeit, diese Lücke zu schließen. Dieser Beitrag ist eine Einführung in Cucumber, ein BDD-Framework mit dem sich (unter anderem) sehr lesbare, wartbare und elegante Akzeptanz-Tests für Web-Anwendungen schreiben lassen.

Dies ist der erste Teil eines zweiteiligen Tutorials zu Cucumber. In diesem Teil geht es vor allem um das Setup, des Weiteren werden die Grundlagen von Cucumber und Gherkin erklärt. Wer bereits mit Cucumber vertraut ist, kann auch direkt zum zweiten Teil springen, in dem gezeigt wird, wie man mit Cucumber, Capybara, Poltergeist und PhantomJS Akzeptanztests für Web-Anwendungen schreibt.

Cucumber hat einen recht interessanten Stammbaum: JBehave wurde als RBehave auf Ruby portiert, RBehave wiederum wurde neu implementiert und in RSpec umbenannt. Zu RSpec wurde der RSpec Story Runner hinzugefügt, aus dem sich Cucumber entwickelt hat. Historisch betrachtet stammt Cucumber also entfernt von JBehave ab. Cucumber (ursprünglich in Ruby implementiert) ist später noch mit Cucumber JVM von Ruby zurück auf die JVM portiert worden, womit sich der Kreis schließt. Wenn man gerade auf der Suche nach einem JVM-basierten BDD-Framework ist, sollte man sich sowohl JBehave als auch Cucumber JVM anschauen. (Auf Stackoverflow gibt es einen knappen und etwas oberflächlichen Vergleich.)

Cucumber wurde außerdem noch auf ein Vielzahl anderer Plattformen portiert. Heutzutage lässt sich Cucumber mit Ruby (sozusagen das Original), Java, .NET, Adobe Flex, Python, Perl, Erlang und PHP benutzen. Man könnte sogar die selben Gherkin-Features (siehe unten) auf verschiedenden Plattformen ausführen, allerdings ist das eher eine theoretische Möglichkeit, denn die Step-Implementierungen (dazu kommen wir noch) sind jeweils Plattform-spezifisch.

Wie die meisten BDD-Frameworks wird Cucumber selten alleine benutzt. Insbesondere wenn man mit dem Werkzeug ernsthaft Web-Applikationen testen möchte, benötigt man einen kompletten Stack, der (mindestens) noch einen Browser-Treiber und eben einen Browser umfasst. In den meisten Fällen wird man den Browser-Treiber auch nicht direkt verwenden wollen, sondern eine Abstraktions-Schicht, die auf einem (oder verschiedenen) Browser-Treibern aufbaut – dies macht den Test-Code sehr viel lesbarer und wartbarer.

Selbst für erfahrene Akzeptanztester sind die Vielzahlt der möglichen Kombinationen in diesem Bereich manchmal etwas unübersichtlich. Allein auf der Ruby-Plattform könnte man unter anderem folgende Bibliotheken mit Cucumber kombinieren:

  • Selenium WebDriver (mit dem selenium-webdriver gem),
  • Watir WebDriver (baut auf Selenium WebDriver auf),
  • Capybara und Webrat,
  • Capybara und Selenium WebDriver oder
  • Capybara und Poltergeist (ein Treiber für PhantomJS)

…und selbst das ist nur eine Auswahl. Zusätzlich können einige der genannten Treiber (z. B. Selenium) verschiedene Browser steuern, so dass man sich noch entscheiden muss, ob man headless testen möchte (etwa mit PhantomJS oder Steam) oder ob man einen oder mehrere “echte” Browser wie Firefox, Chrome oder IE zum Testen verwenden möchte. Fazit: Wenn man Cucumber für ein Projekt evaluiert, muss man letzten Endes auch einen (oder sogar mehrere) komplette Stacks evaluieren, was die Auswahl nicht unbedingt vereinfacht.

Dieses Tutorial verwendet Cucumber auf Ruby und im zweiten Teil zusätzlich noch Capybara, Poltergeist und PhantomJS. Meiner Erfahrung nach funktioniert diese Kombination sehr gut, besonders mit Seiten die viel mit Ajax arbeiten.

Das Tutorial ist eher für Leser geschrieben, die bisher noch keine oder nur wenig Erfahrung mit Ruby haben. Der erste Teil beschreibt die Installation und Konfiguration von Cucumber und wie man sein erstes Cucumber Feature schreibt. Der zweite Teil zeigt dann, wie man Cucumber und Capybara benutzt, um elegante Akzeptanztests für Web-Anwendungen zu schreiben.

Ruby, Cucumber und PhantomJS einrichten

Zuerst sollte das Beispiel-Projekt geklont werden, welches unter anderem den Gemfile enthält, der für das Einrichten von Cucumber benötigt wird:

git clone -b 00_setup https://github.com/basti1302/audiobook-collection-manager-acceptance.git

Um Cucumber und PhantomJS auf einem System einzurichten, kann eine der folgenden Gists benutzt werden. PhantomJS wird erst im zweiten Teil dieses Tutorial benötigt, kann also auch erst später installiert werden.

Wenn das Setup komplett ist, kann die Cucumber-Installation getestet werden, indem man im Hauptverzeichnis des Beispiel-Projekts cucumber eingibt. Das sollte die folgende Ausgabe erzeugen:

0 scenarios
0 steps
0m0.000s

Hinweis: Da wir den Ruby Bundler benutzen, sollten wir eigentlich bundle exec cucumber als Kommando benutzen (statt cucumber). Dies stellt sicher, dass das cucumber Kommando im Kontext des im Gemfile definierten Bundles ausgeführt wird. Solange nicht verschiedene Versionen eines Ruby Gems installiert sind, sollte das keinen Unterschied machen. Wenn Cucumber sich jedoch unerwartet verhält, sollte man es mit bundle exec cucumber versuchen – oder man gewöhnt sich einfach von Anfang an daran, immer bundle exec cucumber zu verwenden.

Cucumber Grundlagen

Features und Scenarios

Die Hauptartefakte von Cucumber werden Features genannt. Sie werden in einer DSL namens Gherkin geschrieben, die mehr oder weniger an natürlich Sprache angelehnt ist (wie natürlichsprachlich ein Feature daher kommt, hängt natürlich wesentlich vom Autor des Features ab.)

Die Default-Sprache für Cucumber/Gherkin ist natürlich Englisch, bei Bedarf können auch andere Sprachen benutzt werden. Dazu reicht es aus, dem Feature ein Language-Tag voranzustellen, etwa # language: de für Deutsch. Dieses Tutorial macht von dieser Möglichkeit allerdings keinen Gebrauch sondern arbeitet mit englischsprachigen Features.

Feature-Dateien haben die Endung .feature und befinden sich üblicherweise im Verzeichnis features. Wenn Cucumber ohne Parameter gestartet wird, sucht es automatisch nach einem Unterverzeichnis diesen Namens im aktuellen Verzeichnis, deshalb empfiehlt es sich, Cucumber im Verzeichnis eine Ebene über features zu starten.

Ein Feature enthält eine Beschreibung (Freitext, der nicht geparst wird) und ein oder mehrere Szenarios. Ein Szenario beschreibt einen Verhaltensaspekt des zu testenden Systems. Es trifft Aussagen darüber, wie sich das System verhält, wenn ein bestimmte Aktion ausgeführt wird – vorausgesetzt, dass bestimmte Vorbedingungen erfüllt sind.

Ein Szenario besteht aus einzelnen Schritten (Steps). Jeder Schritt ist ein Satz (oder Satzteil), der zu einer der drei bekannten BDD-Kategorien gehört:

  • Given (stellt eine Vorbedingung für das Szenario her),
  • When (führt eine Aktion im zu testenden System aus) and
  • Then (verifiziert das gewünschte Ergebnis)

Auf technischer Ebene ist es egal, ob man einem Schritt Given, When, Then, And oder But voranstellt (And und But können für alle drei Kategorien verwendet werden). Man könnte also Vorbedingungen mit Then herstellen und mit Given das gewünschte Ergebnis überprüfen, aber Szenarios, die die Konventionen korrekt verwenden, sind natürlich wesentlich lesbarer.

Benutzt man das oben erwähnte Language-Tag für eine deutsche Lokalisierung, lauten die Präfixe

  • Given: Angenommen/Gegeben sei/Gegeben seien
  • When: Wenn
  • Then: Dann
  • And: Und
  • But: Aber

Für jeden Step muss eine passende Step Definition existieren. Diese liegen im Verzeichnis features/step_definitions. Wir sehen uns diese später im Detail an.

Das erste Feature

Um das erste Cucumber-Feature zu erstellen, legen wir die Datei features/first.feature mit folgendem Inhalt an (statt den Code zu kopieren, kann auch der vorbereitete Branch benutzt werden: git checkout 01_first_feature).

Hinweis: Sämtlicher Code aus diesem Tutorial ist im Beispiel-Repository enthalten. Das Repository enthält mehrere Branches, die ungefähr den einzelnen Abschnitten des Tutorials entsprechen. Eine Liste der vorhandenen Branches lässt sich mit git branch -r anzeigen. Die Namen der Branches werden jeweils im weiteren Verlauf des Tutorials erwähnt, wenn sie relevant werden.

#encoding: utf-8
Feature: Showcase the simplest possible Cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)
 
  Scenario: Cutting vegetables
    Given a cucumber that is 30 cm long
    When I cut it in halves
    Then I have two cucumbers
    And both are 15 cm long

Führt man dieses Feature nun aus, indem man im Hauptverzeichnis des Projekts (das Verzeichnis direkt oberhalb von features) das Kommando cucumber (oder besser: bundle exec cucumber) absetzt, sollte die Ausgabe in etwa so aussehen:

#encoding: utf-8
Feature: Showcase the simplest possible cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)

  Scenario: Cutting vegetables          # features/first.feature:8
    Given a cucumber that is 30 cm long # features/first.feature:9
    When I cut it in halves             # features/first.feature:10
    Then I have two cucumbers           # features/first.feature:11
    And both are 15 cm long             # features/first.feature:12

1 scenario (1 undefined)
4 steps (4 undefined)
0m0.003s

You can implement step definitions for undefined steps with these snippets:

Given(/^a cucumber that is (\d+) cm long$/) do |arg1|
  pending # express the regexp above with the code you wish you had
end

When(/^I cut it in halves$/) do
  pending # express the regexp above with the code you wish you had
end

Then(/^I have two cucumbers$/) do
  pending # express the regexp above with the code you wish you had
end

Then(/^both are (\d+) cm long$/) do |arg1|
  pending # express the regexp above with the code you wish you had
end

If you want snippets in a different programming language,
just make sure a file with the appropriate file extension
exists where cucumber looks for step definitions.

Cucumber wiederholt zuerst das Feature, danach wird das Resultat ausgegeben. Cucumber beschwert sich hier zurecht darüber, dass wir die benutzten Schritte noch nicht definiert haben. Als besonderen Service gibt Cucumber noch Vorschläge aus, wie wir die Schritte implementieren können. Wie nett! Es vermutet außerdem noch (korrekterweise), dass die Zahlen in den Schritten variabel sind.

Wir können also Cucumbers Vorschläge versuchsweise in eine Step Definition Datei kopieren. (Tatsächlich führe ich Cucumber ab und an absichtlich mit undefinierten Schritten aus, um die Vorschläge als Vorlagen für die Step Definitions zu verwenden.)

Steps und Step Definitions

Legen wir also das Verzeichnis features/step_definitions an und in diesem Verzeichnis die Datei first_steps.rb. Step Definitions werden in Ruby implementiert – zumindest in der Ruby-Variante von Cucumber. Wie bereits erwähnt gibt es Cucumber-Portierungen für einer Vielzahl von Programmiersprachen, es besteht also durchaus die Möglichkeit, Step Definitions in der Sprache zu implementieren, mit der man am vertrautesten ist. In diesem Beitrag wird allerdings das “Original” und damit Ruby verwendet. Auch wer bisher noch nichts mit Ruby zu tun hatte, hat keinen Grund zur Besorgnis: Step Definitions müssen nicht allzu kompliziert sein und wir werden die benötigten Ruby-Sprachmittel unterwegs erläutern.

Hier nun der Code für features/step_definitions/first_steps.rb. (Der Name der Datei ist eigentlich irrelevant, alle .rb Dateien in features/step_definitions werden herangezogen.)

#encoding: utf-8
Given /^a Cucumber that is (\d+) cm long$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end
 
When /^I cut it in havles$/ do
  pending # express the regexp above with the code you wish you had
end
 
Then /^I have two Cucumbers$/ do
  pending # express the regexp above with the code you wish you had
end
 
Then /^both are (\d+) cm long$/ do |arg1|
  pending # express the regexp above with the code you wish you had
end

Wenn wir nun Cucumber erneut ausführen, sieht die Ausgabe schon geringfügig anders aus (nicht unbedingt besser, aber anders):

#encoding: utf-8
Feature: Showcase the simplest possible cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)

  Scenario: Cutting vegetables          # features/first.feature:8
    Given a cucumber that is 30 cm long # features/step_definitions/first_steps.rb:1
      TODO (Cucumber::Pending)
      ./features/step_definitions/first_steps.rb:2:in `/^a cucumber that is (\d+) cm long$/'
      features/first.feature:9:in `Given a cucumber that is 30 cm long'
    When I cut it in halves             # features/step_definitions/first_steps.rb:5
    Then I have two cucumbers           # features/step_definitions/first_steps.rb:9
    And both are 15 cm long             # features/step_definitions/first_steps.rb:13

1 scenario (1 pending)
4 steps (3 skipped, 1 pending)
0m0.005s

Die Zusammenfassung informiert uns, dass mindestens einer der Schritte den Status pending hat und daher das ganze Szenario als pending markiert wird. Ein Szenario wird komplett übersprungen, wenn einer seiner Schritte fehlschlägt oder als pending markiert ist, daher versucht Cucumber erst gar nicht, die anderen Schritte auszuführen, sondern zählt die verbleibenden drei Schritte als skipped.

Bevor wir nun die Step Definitions tatsächlich mit Leben füllen, sollten wir uns first_steps.rb etwas näher anschauen. Wenn man noch nie zuvor Cucumber Step Definitions gesehen hat, sieht der Code vielleicht etwas ungewöhnlich aus. Die einzelnen Step Definitions sehen ein wenig wie Methoden aus – tatsächlich sind sie nichts anderes als Ruby-Methoden und als Code innerhalb der einzelnen Step Definitions lässt sich normales Ruby verwenden. Die Methoden-Signaturen sehen allerdings seltsam aus – sie enthalten jeweils einen regulären Ausdruck. Das ist ein nützliches Feature: Wenn Cucumber nach der Step Definition für einen Step in einem Szenario sucht, werden alle Step Definition Dateien (alle .rb Dateien in features/step_definitions, inklusive Unterverzeichnisse) durchsucht. Wird ein passender regulärer Ausdruck gefunden, wird die dazugehörige Step Definition ausgeführt. Übrigens: Wenn Cucumber mehr als einen passenden regulären Ausdruck findet, beschwert es sich (ambiguous match), statt einfach die erstbeste Definition auszuführen.

Dieser Mechanismus bringt mehrere Vorteile mit sich. Einer dieser Vorteile ist eine gut lesbare und flexible Parametrisierung von Step Definitions. Wir haben das bereits gesehen:

Given /^a Cucumber that is (\d+) cm long$/ do |arg1|

Der Teil der Zeichenkette, der der Capturing Group – (\d+) – zugeordnet wurde, wird der Step Definition als Parameter arg1 übergeben (wir sollten diese Parameter vermutlich in length umbenennen). \d+ passt zu einer oder mehreren Ziffern, somit kann hier also ein numerischer Wert übergeben werden.

Außerdem eröffnet die Verknüpfung von Steps mit Step Definitions über reguläre Ausdrücke die Möglichkeit, Szenarios zu schreiben, die sich sehr flüssig lesen lassen, ohne dafür Step Definitions duplizieren zu müssen. Dazu ein kleines Beispiel: Wir könnten den zweiten Step folgendermaßen umschreiben:

When /^I (?:cut|chop) (?:it|the Cucumber) in (?:halves|half|two)$/ do
  pending # express the regexp above with the code you wish you had
end

Damit wäre jeder der folgenden Schritte möglich:

When I cut the Cucumber in halves
When I chop the Cucumber in half
When I cut it in two

(In Bezug auf das Zerkleinern von Gemüse ist “cutting in halves” besseres Englisch als “cutting in half”, aber darum geht es hier nicht.)

Wir benutzen hier Non-Capturing Groups (diese fangen mit "(?:") an) um die verschiedene Formulierungen zu unterstützen.

Wenn man allerdings einmal damit anfängt, seine Step Definitions dergestalt zu erweitern, sollte man darauf achten, es nicht zu übertreiben, sondern nur die Variationen bereitzustellen, die man auch tatsächlich gerade benötigt – sonst läuft man Gefahr, sich mehr mit den regulären Ausdrücken auseinanderzusetzen, statt sein System zu testen. Außerdem hat man noch weitere Möglichkeiten, Duplizierungen bei den Step Definitions zu vermeiden. Man kann normale Ruby-Methoden in den Step File einfügen und diese von den Step Definitions aus aufrufen. Weiterhin kann man andere Ruby-Module per require einbinden und deren Code so wiederverwenden. Schließlich lassen sich sogar Step Definitions von anderen Step Definitions aus aufrufen.

Step Definitions implementieren

Momentan enthalten alle Steps Definitions nur einen Aufruf der Methode pending. Dies ist im Prinzip ein Todo-Marker. Mit pending lässt sich das Szenario zuerst ohne implementierte Step Definitions schreiben und dann ein Step nach dem anderen definieren, bis das Szenario erfolgreich durchläuft. Dieses Vorgehen passt auch gut zu einem ATDD-/Outside-In-Ansatz, bei dem man die Implementierung des Systems mit Akzeptanztests treibt:

  1. Zuerst das Cucumber-Feature schreiben (mit pending Steps),
  2. die Step Definition des ersten Steps implementieren,
  3. genau die Funktionalität im getesteten System implementieren, die benötigt wird, damit dieser Step durchläuft,
  4. diesen Ablauf jeweils für die restlichen Steps wiederholen.

Nach langer Vorrede nun die Implementierung der Step Definitions. Der Code ist auch per git checkout 02_step_definitions verfügbar.

Given /^a Cucumber that is (\d+) cm long$/ do |length|
  @cucumber = {:color =&gt; 'green', :length =&gt; length.to_i}
end
 
When /^I (?:cut|chop) (?:it|the cucumber) in (?:halves|half|two)$/ do
  @choppedCucumbers = [
    {:color =&gt; @cucumber[:color], :length =&gt; @cucumber[:length] / 2},
    {:color =&gt; @cucumber[:color], :length =&gt; @cucumber[:length] / 2}
  ]
end
 
Then /^I have two cucumbers$/ do
  @choppedCucumbers.length.should == 2
end
 
Then /^both are (\d+) cm long$/ do |length|
  @choppedCucumbers.each do |cuke|
    cuke[:length].should == length.to_i
  end
end

Wenn Cucumber nun erneut ausgeführt wird, sollte folgendes Ergebnis ausgegeben werden:

#encoding: utf-8
Feature: Showcase the simplest possible cucumber scenario
  In order to verify that cucumber is installed and configured correctly
  As an aspiring BDD fanatic 
  I should be able to run this scenario and see that the steps pass (green like a cuke)

  Scenario: Cutting vegetables          # features/first.feature:8
    Given a cucumber that is 30 cm long # features/step_definitions/first_steps.rb:3
    When I cut it in halves             # features/step_definitions/first_steps.rb:7
    Then I have two cucumbers           # features/step_definitions/first_steps.rb:14
    And both are 15 cm long             # features/step_definitions/first_steps.rb:18

1 scenario (1 passed)
4 steps (4 passed)
0m0.005s

Grandios! Nun werden alle Schritte in grün ausgegeben – damit sieht die Ausgabe (mit viel Fantasie) wie eine Cucumber aus (wodurch auch erklärt wäre, wie das Framework zu seinem Namen kommt). Betrachten wir die Implementierung der Schritte im Einzelnen: Ein Bezeichner, der mit einem @ beginnt, ist in Ruby eine Instanz-Variable. Diese müssen nicht vorab deklariert werden; wenn sie zur Laufzeit zugewiesen werden, ist die Instanzvariable ab diesem Zeitpunkt definiert. Damit ist @cucumber in allen Schritten sichtbar und wir können Zustand über Step-Grenzen hinweg halten. Die Ausdrücke, die in geschweifte Klammern gefasst sind, sind Hashes (aka Dictionaries oder assoziateve Arrays, vergleichbar mit java.util.Map); die Bezeichner, die mit einem Doppelpunkt beginnen, sind hier die Schlüssel (der Doppelpunkt macht sie zu Symbolen, aber das ist momentan nebensächlich).

Die Ausdrücke @choppedCucumbers.length.should == 2 und cuke[:length].should == length.to_i bedürfen evtl. einer Erläuterung. Wir benutzen hier das RSpec Modul Spec::Expectations. In features/support/env.rb haben wir die Zeile require 'rspec/expectations', dadurch ist dieses Modul in jedem unserer Step Files (also auch first_steps.rb) verfügbar. (Alle Dateien in features/support/ werden automatisch vor dem Ausführen des ersten Szenarios geladen.) Mit diesem Modul haben wir die Möglichkeit, Erwartungen an beliebige Objekte zu formulieren, in dem es allen Objekten Methoden hinzufügt (Monkey Patching). In diesem Fall benutzen wir Object Expectations, damit stehen uns auf allen Objekten die Methoden should == sowie should_not == (und noch einige mehr) zur Verfügung. Eigentlich ist das aber nichts anderes als Syntactic Sugar, eine etwas elegantere Möglichkeit um unsere Erwartung auszudrücken, das ein bestimmter Wert gleich einem zweiten Wert ist. Dies ist möglich, da die besagten Methoden allen Objekten hinzugefügt werden und weil in Ruby tatsächlich alles ein Objekt ist, selbst numerische Werte wie die Länge eines Arrays – daher können wir die Method should == auf der Eigenschaft length des Arrays @choppedCucumbers aufrufen.

Die Implementierungen der Step Definitions sind recht albern – tatsächlich ist das ganze Szenario hinreichend albern, außerdem haben wir in diesem Beispiel noch nicht mal produktiven Code getestet. Sämtlicher Code befindet sich im Step File. Trotzdem sollten anhand des Beispiels einige grundlegende Konzepte von Cucumber klar geworden sein.

Anmerkung: Wir mussten an zwei Stellen die Methode to_i benutzen, da der Parameter, der durch die Capturing Group im regulären Ausdruck entgegengenommen wird, immer ein String ist. Wir könnten diese Unschönheit umgehen, indem wir Step Argument Transformer benutzen.

Gut, und jetzt?

Damit sind die Grundlagen von Cucumber abgedeckt – und wir können aufhören, mit albernen Beispielen zum Halbieren von Gurken herumzuspielen. Im zweiten Teil werden wir eine echte Web-Anwendung testen.

The post Cucumber: Setup und Grundlagen appeared first on codecentric Blog.

ALE13 – Geschichten aus Bukarest

$
0
0

Geschichten aus Bukarest

Drei Tage lang tummelten sich in Bukarest um die 150 Agilisten aus ganz Europa, um an der diesjährigen Unkonferenz des ALE-Netzwerks teilzunehmen. Wie auch in den vergangenen Jahren hat codecentric diese bemerkenswerte Veranstaltung wieder als Sponsor unterstützt. Und dieses Mal durfte ich dann auch einen Punkt auf die Teilnehmer-Landkarte kleben.

ALE13 Teilnehmer-Landkarte

Agilisten aus ganz Europa trafen sich in Bukarest zur ALE13

Nachdem ich auf der XP 2011 in Madrid bei der Erarbeitung der Vision des ALE-Netzwerks dabei sein durfte, war meine erste Teilnahme bei einer ALE-Unkonferenz ein ganz besonderes Erlebnis. Ich finde es immer wieder bemerkenswert, zu sehen, was eine selbstorganisierte Gruppe mit einem gemeinsamen Ziel zu leisten imstande ist.

Etwas für jeden Geschmack

Wie es sich für diese bunt zusammengewürfelte Community gehört, beinhaltete das Programm Vorträge und Workshops für jeden Geschmack.

Angeleitet von Johan Martinsson und Remy Sanlaville konnten Softwareentwickler beispielsweise erfahren, wie es sich anfühlt, Object Calisthenics (eine Übung, in mittels der Anwendung sehr strikter Einschränkungen objektorientiertes Design erforscht wird) auf das Refaktorieren von Bestandscode anzuwenden.

Coaches lernten die Grundlagen des Archetype Feedbacks (einer in der Temenos-Methode verwendeten Praktik) und setzten sich mit kognitiven Verzerrungen auseinander. In einem von Angel Diaz-Maroto Alvarez unterstützten Workshop wendeten sie Design Thinking als Hilfsmittel zur Erforschung kultureller Widerstände bei Agilen Transformationen an.

Für diejenigen, die sich für neue Wege der Zusammenarbeit mit ihren Kunden interessieren, gaben Jurgen Appelos Keynote und Kurt Häuslers Präsentation zu agilen Preis- und Dienstleistungsmodellen interessante Denkanstöße.

Aus Sicht eines Anbieters von Softwareentwicklungs-Dienstleistungen waren insbesondere diese Ideen wertvoll. Wie Sie vermutlich bereits erleben konnten, unterliegt agile Softwareentwicklung fundamental anderen Gesetzen als tradierte Verfahren. Einen Weg zu finden, Projekte zu initialisieren, ohne die agilen Prinzipien zu kompromittieren bietet allen Beteiligten die Gelegenheit, besser von dem Wertversprechen dieser Vorgehensweise zu profitieren.

Falls Sie sich für mehr Details interessieren, empfehle ich einen Blick auf die Videos auf der ALE13-Webseite. Insbesondere die Abschluss-Keynote über eXtreme Manufacturing von Joe Justice aus dem Team Wikispeed, die Standing Ovations erhielt, ist sehenswert.

Open Space

ALE13 Open Space Programm

Teilnehmer bringen ihre eigenen Ideen zum Open Space

Während des Open Space, der an jedem Nachmittag der drei Konferenztage stattfand, hatte Teilnehmer die Gelegenheit, das Programm mit ihren eigenen Themen zu bereichern. Unter meinen persönlichen Favoriten waren Liz Keoghs Überblick über Praktiken rund um BDD und Angel Medinillas Einführung ins Sketchnoting.

Außerdem hatte ich die Gelegenheit, einige meiner Erfahrungen mit Gojko Adzics Impact-Mapping-Methode weiterzugeben (falls Sie sich mit diesem Thema beschäftigen und die diesjährige W-JAX besuchen, interessiert Sie vielleicht mein Vortrag auf dem Agile Day).

Fazit

Interessante Vorträge, ein toller Open Space und ein perfekt organisiertes Rahmenprogramm haben die diesjährige ALE-Unkonferenz zu einer ganz besonderen Veranstaltung gemacht. Ich bin sicher, dass mich die vielen neuen Anregungen in den nächsten Monaten immer wieder beschäftigen werden. Falls Sie also auf der Suche nach agilem Gedankenfutter sind, sollten Sie die Webseite des ALE Netzwerks im Auge behalten und die ALE14 nicht verpassen.

Bis dahin: Free Spirit!

The post ALE13 – Geschichten aus Bukarest appeared first on codecentric Blog.

Wenn’s mal wieder länger dauert… Profiling Robot Framework

$
0
0

Tests, die man typischerweise mit Hilfe des Robot Framework realisiert (z.B. User Acceptance Tests), neigen dazu, nicht ganz so flott durchzulaufen wie vielleicht Unit-Tests. In aller Regel reden wir hier von Stunden und nicht mehr von Minuten. Egal, ob man diese Tests vielleicht in die Nacht verschieben kann, lästig ist es trotzdem. Auch die parallele Ausführung auf mehreren Testclients bekämpft nur die Symptome.

Wie man es auch nimmt, von Zeit zu Zeit wäre es einfach verdammt nützlich feststellen zu können, wo der Robot die Zeit liegen lässt. Zeit für Profiling!

Das standardmäßig erstellte output.xml-Log-File enthält eigentlich alle benötigten Informationen. Jedes ausgeführte Keyword ist dort mit einem Tag enthalten. Dieses Tag enthält als geschachteltes Element ein status-Tag, in dem die Anfangs- und Endzeit der Keyword-Ausführung verzeichnet sind. Fehlen also nur noch Werkzeuge zur Aufbereitung und Auswertung dieses Informationen…

time2csv.py aus dem Robot Framework

Ein Werkzeug ist Bestandteil des Robot Framework, wird allerdings nur in der Source-Distribution ausgeliefert. Das Python-Script “times2csv.py” berechnet die Laufzeit jedes Keywords und stellt es innerhalb der Aufrufhierarchie dar. Die Daten werden als CSV-Datei geschrieben und eignen sich gut für eine Darstellung und Auswertung in Excel oder einer anderen Tabellenkalkulationen.

Beispielausgabe von time2csv

Beispiel für die Ausgabe von times2csv

Diese Form der Auswertung ist natürlich dann gut geeignet, wenn man einen konkreten Abschnitt seiner Testsuite analysieren will. Man muss also bereits den konkreten “Flaschenhals” entdeckt oder zumindest einen Verdächtigen bestimmt haben. Ansonsten geht man in den Informationen unter, insbesondere bei umfangreichen Testsuiten. (Bei der Auswertung eines typischen Logs in einem Projekt hatten die erstellten drei Dateien insgesamt ~2,3 Mio. Zeilen Umfang, die größte mit fast 900.000 Zeilen…)

Robot Framework mit robot_profiler.py analysieren

Was aber tun, wenn man noch keine Ahnung hat, wo die Zeit bleibt oder wenn man den Verdacht hat, in einigen Keywords immer nur etwas Zeit zu verlieren, was sich dann erst durch eine hohe Aufrufanzahl negativ auswirkt? Für diese Situation braucht man eine andere Auswertung: Die aufsummierte Laufzeit je Keyword und die durchschnittliche Laufzeit je Keyword.

Für diesen Zweck habe ich ein kleines Python-Script geschrieben. Es ist in GitHub verfügbar.

Das Script kennt die folgenden Kommandozeilenparamenter:

usage: robot_profiler.py [-h] [-e ENCODING] [-s SEPARATOR] [-l LOCALE]
                         input_file_name [output_file_name]

positional arguments:
  input_file_name       The output.xml file to read from.
  output_file_name      The file to write the profiler data to.

optional arguments:
  -h, --help            show this help message and exit
  -e ENCODING, --encoding ENCODING
                        Encoding for output file.
  -s SEPARATOR, --separator SEPARATOR
                        Separator used in output file.
  -l LOCALE, --locale LOCALE
                        Locale used for number formatting.

Wird keine Ausgabedatei angegeben, wird der Name (und Pfad) der Eingabedatei genommen und die Extension auf ‘.csv’ gesetzt.

Die Parameter -e, -s und -l sind optional. Hier kann das Encoding, der Spaltentrenner und die gewünschte Lokalisierung für das Zahlenformat festgelegt werden. Die Default-Werte sind so festgelegt, dass die Ausgabedatei als CSV-Datei direkt mit Excel geöffnet werden kann (wohl der häufigste Anwendungsfall…): Als Encoding wird cp1252 verwendet (also Windowsstandard) und als Trenner wird das Semikolon eingesetzt (im Gegensatz zum Komma wird es von Excel beim Öffnen mit Doppelklick als Trenner erkannt). Wenn man eine TSV-Datei (tab separated values) benötigt, so kann man das mit -s \t erreichen. Die Lokalisierung hat keinen expliziten Default, so dass das Script den Plattformstandard verwendet. Bei der Lokalisierung ist zu beachten, dass die Werte u.U. plattformabhängig sind. So ist “Deutsch” unter Linux als de_DE kodiert, unter Windows aber als German

Für die Ausführung dieses Scripts mit Output-Dateien von ‘echten’ Testläufen (also alles, was nicht zu trivialen Testzwecken gedacht ist) würde ich immer natives Python empfehlen, denn leider ist Jython hinsichtlich des Speicherverbrauches wohl eher ineffizient und langsam (falls es überhaupt durchläuft und nicht mit einem Fehler aussteigt…). Falls kein Python zur Hand ist oder man Python nicht extra installieren will, kann man immer noch auf PortablePython ausweichen.

Das Script erzeugt eine Ausgabedatei, die in aller Regel mit einem Doppelklick in MS Excel geöffnet werden kann. Neben dem (vollqualifizierten) Keyword-Bezeichner sind die Anzahl der Aufrufe, die Gesamtausführungszeit und die durchschnittliche Ausführungszeit aufgeführt.

Beispielausgabe von robot_profiler.py

Beispiel für die Ausgabe von robot_profiler.py

Wenn man sich nun auf die Suche nach einem Flaschenhals macht, würde ich vorschlagen, die Liste in eine Tabellenkalkulation zu laden, nach der durchschnittlichen Laufzeit absteigend zu sortieren und dann alle Durchschnittswerte kleiner 1 Sekunde wegfiltern zu lassen. Die Ergebnisliste ist dann hoffentlich übersichtlich genug für eine eingehende Untersuchung.

Viel Spaß beim Testen :-)

Links/h3>

The post Wenn’s mal wieder länger dauert… Profiling Robot Framework appeared first on codecentric Blog.

Gibt es intrinsische Motivatoren für Unternehmen?

$
0
0

Einem Softwaredienstleister stellt sich immer wieder die Frage, mit welchem grundsätzlichen Bezahlmodell man seine Dienstleistung an einen Kunden verkauft. Einen interessanten Einstieg in diese Diskussion bietet der Blogartikel „Pricing, And A Little Bit About Estimation“ von Kurt Häusler. Auf der Suche nach einem guten Bezahlmodell betrachtet er, wie unterschiedliche Modelle zu „schlechtem“ Verhalten motivieren.

Dabei kam mir der Gedanke, dass ich vergleichbare Überlegungen auch bei der Frage nach Bonus und Vergütung von Firmenmitarbeitern kenne. Also: Welches Vergütungssystem begünstigt bzw. benachteiligt gewünschtes Verhalten der Mitarbeiter? Oder sogar: Welches Bonussystem erzeugt „gutes“ Verhalten ,und welches „schlechte“ Verhalten erzeugt es als Nebenwirkung?

Mein aktueller Stand dieser Diskussionen ist, dass Versuche, Leistungsanreize über die Vergütungs- und Bonussysteme zu setzen, im Bereich der Softwareentwicklung meist mehr negative Nebenwirkungen als positive Effekte haben. Geld ist in vielen Fällen zwar der primäre Grund aus dem Mitarbeiter für Firmen arbeiten, deshalb müssen sie gut genug bezahlt werden (was das genau heißt, ist eine eigenständige Diskussion), aber die Leistungsbereitschaft der Mitarbeiter wird besser und stärker durch andere (nicht monetäre) Faktoren beeinflusst. Welche dieser Motivatoren einen Mitarbeiter effektiver ansprechen und welche für ihn eher sekundär sind, ist von Mitarbeiter zu Mitarbeiter verschieden. Siehe hierzu Jurgen Appelos Artikel „CHAMPFROGS… The 10 Intrinsic Desires“.

Was bedeutet das jetzt für das Verhältnis auf der B2B Ebene zwischen Softwaredienstleister und Kunde? Der primäre Grund des Dienstleisters (und oft auch des Kunden) die gemeinsame Aufgabe anzugehen ist, Geld zu verdienen um wirtschaftlich erfolgreich zu sein. Aber ich vermute, dass es ebenfalls weitere nicht monetäre Bedürfnisse eines Unternehmens gibt, die das Verhalten des Unternehmens beeinflussen, vergleichbar mit Jurgens “Intrinsic Desires”. Teilt Ihr diese Vermutung oder denkt Ihr, dass man Begriffe wie “Motivation” oder “Bedürfnis” nicht auf abstrakte Konstrukte ohne natürliches Bewusstsein wie Unternehmen anwenden darf.

Wenn man davon ausgeht, dass das Konzept für Organisationen zulässig ist, fallen mir folgende Bedürfnisse ein, die als mögliche Motivatoren dienen können:

  • Sicherheit/Kontinuität - Auch in Zukunft sind wir für unsere Mitglieder und Geschäftspartner da.
  • Prestige/Ruhm – Wir sind in aller Munde, wenn es in Gesprächen um unser Thema geht.
  • Wachstum – Wir gewinnen an Größe.
  • Flexibilität - Wir können mit den Veränderungen der Welt umgehen und nutzen sie zu unserem Vorteil.
  • Sinnhaftigkeit/Wertschaffung – Die Ergebnisse, die wir schaffen, sind sinnvoll und stellen einen Wert dar.
  • Einfluss/Macht – Wir sind in der Lage, die Welt um uns herum zu verändern.
  • Netzwerk/Beziehungen – Es gibt viel Personen und Organisationen mit denen wir Kontakt pflegen.
  • Fürsorge – Wir kümmern uns um unsere Mitglieder, aber auch um unsere Geschäftspartner und Kunden.

Wie bei Einzelpersonen auch, sind die unterschiedlichen Motivatoren für ein Unternehmen auch unterschiedlich wirksam. Um die Zusammenarbeit zwischen Unternehmen besser zu gestalten, ist es hilfreich, sich über diese Priorisierung der Motivatoren der beteiligten Unternehmen bewusst zu sein. So kann die geplante gemeinsame Aufgabe so gestaltet werden, dass wichtige Motivatoren beider Seiten angesprochen werden. Die Priorisierung der Motivatoren gegenüber den anderen Parteien offen zu legen, erfordert aber auch ein hohes Maß an Vertrauen.

Dieser erste Versuch, Bedürfnisse von Unternehmen aufzulisten, erhebt keinen Anspruch auf Vollständigkeit. Wenn Ihr Vorschläge für weitere mögliche Bedürfnisse habt oder Euch ganz andere für Unternehmen in den Sinn kommen, dann würde ich mich freuen, sie mit Euch zu diskutieren und zusammen zu tragen.

The post Gibt es intrinsische Motivatoren für Unternehmen? appeared first on codecentric Blog.


Wie man zu besseren Ideen kommt?

$
0
0

Haben Sie schon einmal eine gute Idee gehabt?

Könnten Sie sich an einen Moment erinnern, wenn Sie Ihre Ideen für das erste Mal präsentierten?

Was für ein Gefühl hatten Sie ? Hatten Sie Angst vor ungerechtfertigter oder unfairer Kritik?

Können Sie sich an Situationen erinnern, in denen Ihre Idee aufgrund politischer Hintegründe nicht akzeptiert wurde, nicht weil sie schlecht war?

Wenn die Antwort auf eine von diesen Fragen „Ja“ lautet, dann haben Sie vielleicht Interesse an einer Technik, die hilft, eine Idee konstruktiv zu verbessern.
Lassen Sie mich das Perfection Game vorstellen.

Das „Perfection Game“

Das Spiel funktioniert so:

  • Zuerst stellen Sie eine Idee vor.
  • Dann bitten Sie diese Idee auf einer Skala von 1 bis 10 zu beurteilen. Die Beurteilung hängt davon ab, wie weit man die Idee verbessern kann.
  • Teilnehmer präsentieren die Beurteilungen und sagen: “Meine Beurteilung der Idee ist X von 10″.
  • Dann sagt jeder Teilnehmer, was sie für besonders gut in der Idee finden.
  • Der wichtigste Schritt ist die Erklärung jedes Teilnehmers, was notwendig wäre, um 10 von 10 Punkten zu erhalten.

Regeln des Spieles

Es gibt folgende Regeln für das „Perfection Game“:

Wenn Sie konstruktive Vorschläge zur Verbesserung Ihrer Ideen erhalten möchten, seien Sie bereit

  • Alle Vorschläge zu hören.
  • Klärende Fragen zu stellen um mehr Information zu bekommen.
  • Vorschläge zu akzeptieren ohne eine Diskussion zu beginnen oder die Vorschläge direkt abzulehnen .

Wenn Sie als Teilnehmer Ideen im „Perfection Game” beurteilen wollen, dann müssen Sie bereit sein:

  • Eine positive Kritik zu geben und sagen, was gefällt ihnen und was verbessert werden kann.
  • Nicht erwähnen, was Ihnen nicht gefällt, oder gar nicht relevant ist, außer Sie können Verbesserungen vorschlagen
  • Punkte nur dann abzuziehen, wenn Ihnen Verbesserungen einfallen.
  • Grundsätzlich positiv zu sein.
  • Zusätzliche Information und Klärungen zu geben.

Wie funktioniert es in der Praxis?

Nehmen wir an, dass sie zum Beispiel ein Treffen des agilen Stammtisches verbessern wollen. Dann reservieren Sie ca. 30 Minuten am Ende des Treffens und sagen:

„Vielen Dank, dass Sie heute hier sind.

Ich möchte, dass unsere Treffen immer besser, dynamischer und interessanter für alle Teilnehmer werden.

Können Sie bitte Feedback geben und unser heutiges Treffen beurteilen auf einer Skala von 1 bis 10, wobei 1 bedeutet, dass ein Treffen sehr schlecht war und 10 – dass das Treffen wirklich gut und nützlich war.

Bitte ziehen sie die Punkte nur dann ab, wenn Ihnen spezifische Verbesserungen einfallen. Man darf eine verschiedene Anzahl von Punkten ziehen für verschiedene Vorschläge.

Sagen sie mir bitte auch, was Ihnen besonders gefallen hat. “

Eine Beurteilung kann wie folgt aussehen:

„Ich gebe dem Treffen 7 Punkte von 10.

Was mir besonders gefallen hat waren der Platz des Treffens und die Mischung von Teilnehmern.

Um 10 von 10 zu erreichen, würde ich 2 Punkte geben für etwas zu essen während des Treffens und 1 Punkt für besseres Zeitmanagement.

Was ist wirklich wichtig, wenn Sie das „Perfection Game“ spielen?

  • Machen Sie eine Einführung. Erklären Sie die Regeln und geben Beispiele .
  • Bitten sie Teilnehmer, Vorschläge unabhängig vorzubereiten. Erlauben Sie keinen Austausch während der Vorbereitungszeit.
  • Seien Sie aufmerksam und stellen sie Fragen
  • Notieren Sie alle Vorschläge
  • Setzen Sie die relevanten Vorschläge um

Das “Perfection Game” ist nur einen von “Core-Protokollen“, einer Menge von Techniken für Produktivitätsverbesserung im Team, die von Jim McCarthy entwickelt wurden.

Wollen Sie das „Perfection Game“ in der Praxis erfahren, mehr über die Core-Protokolle lernen, oder sind Sie an weiteren Aspekten der Agilität im Allgemeinen und agilen Vorgehensweisen im Speziellen interessiert? Dann sind Sie herzlich eingeladen, am Agilen Stammtisch Frankfurt teilzunehmen.

Der nächste Agile Stammtisch findet am 5 Dezember 2013, um 19:00 Uhr in „Die Zentrale“ statt.

The post Wie man zu besseren Ideen kommt? appeared first on codecentric Blog.

Testautomatisierung mit dem Robot Framework

$
0
0


Ein wenig Geschichte

Das Robot Framework ist ein umfangreiches Werkzeug für die Automatisierung von Tests. Das Robot Framework wurde ursprünglich bei Nokia Siemens Networks entwickelt. Eine erste Version wurde hier bereits im Jahr 2005 für Projekte genutzt. Seit dem Jahr 2008 ist das Projekt Open Source und steht unter der Apache License 2.0 allen Nutzern zur Verfügung.

Als Benutzer der ersten Stunde (zu dieser Zeit habe ich bei Nokia Siemens Networks gearbeitet) habe ich die Entwicklung des Robot Frameworks bis heute verfolgt. Dabei ist der Ansatz der Test-Entwicklung mit Schlüsselwörtern (im Folgenden nutzen wir hierfür den englischen Begriff Keywords) von Anfang an konsequent verfolgt worden und bildet die Stärke und das Rückgrat des Robot Frameworks.

Neben der kontinuierlichen Weiterentwicklung des eigentlichen Frameworks hat der Schritt in Richtung Open Source auch zu einem sehr lebendigen Eco-System rund um das Framework geführt. So gibt es ein sehr aktives Forum, in welchem auch die Core-Entwickler regelmäßig zu finden sind. Weitere wichtige Projekte sind eine grafische Oberfläche für die Entwicklung von Testfällen und eine Vielzahl an fertigen Test-Bibliotheken für die verschiedensten Test-Szenarien.

Testautomatisierung – Der Weg des Robot Frameworks

Das Prinzip der Automatisierung von Tests gab es schon lange bevor das Thema Test-Frameworks wirklich aktuell wurde. Oft wurden Skripte geschrieben, welche dann bestimmte Aspekte einer Anwendung – oft semi-automatisiert – getestet haben. Mit dem verstärkten Aufkommen Agiler Softwareentwicklung und den damit einhergehenden – oft relativ kurzen – Iterationen und häufigerem Testbedarf hat auch die Professionalisierung der Test-Frameworks noch einmal einen Schub bekommen. Im Open Source Umfeld gibt es heute eine Vielzahl an Test-Frameworks. Bekannte Vertreter neben dem Robot Framework sind hier insbesondere Cucumber und FitNesse.

“Checks Are Machine-Decidable; Tests Require Sapience“ – Michael Bolten

Das Robot Framework bietet mit seinem konsequenten Ansatz Tests keyword-driven zu entwickeln hier einen eigenständigen Weg an. Listing 1 zeigt ein einfaches Beispiel für einen mit dem Robot Framework geschriebenen Test.

*** Settings ***
Library           OperatingSystem

*** Test Cases ***
Windows Verzeichnis Nicht Leer
    [Documentation]    Windows-Verzeichnis darf nicht leer sein.
    [Tags]    windows    softwerker
    ${DATEIEN}=    Anzahl Dateien im Windows-Verzeichnis
    Should Not Be Equal As Numbers    0    ${DATEIEN}

*** Keywords ***
Anzahl Dateien im Windows-Verzeichnis
    ${ANZAHL}=    Count Files In Directory    C:\\WINDOWS
    Log    ${ANZAHL}
    [Return]    ${ANZAHL}

Listing 1 – Test-Implementierung mit dem Robot Framework

Dieser Beispiel-Test prüft, dass das Windows-Verzeichnis nicht leer ist. (Der Test kann leicht für ein Unix-Betriebssystem angepasst werden.)

Es geht hier weniger um den Test an sich, als vielmehr um ein einfaches Beispiel, welches den Aufbau der Tests zeigt. Einzelne Testfälle werden im Robot Framework in sogenannten Test Suites zusammengefasst. Hierbei handelt es sich um eine Datei, welche Testfälle beinhaltet. In unserem Beispiel gibt es einen Testfall „Windows Verzeichnis Nicht Leer“ und ein eigenes Keyword „Anzahl Dateien im Windows-Verzeichnis“. In größeren Projekten werden eigene Keywords in sogenannte Resource-Dateien zusammengefasst und ausgelagert. Diese werden dann – analog zu den Test-Libraries im Settings-Bereich einer Test Suite eingebunden. Im Beispiel wird die Operating System Bibliothek des Robot Frameworks genutzt, um Tests auf dem lokalen Dateisystem zu ermöglichen. Auch wird bereits an diesem einfachen Beispiel die Möglichkeit gezeigt Wertzuweisungen zu nutzen. In diesem Fall wird die Variable ${DATEIEN} erzeugt, welche dann wieder als Parameter für weitere Keywords genutzt werden kann. Es gibt verschiedene Formate für die Eingabe der Tests. Das im Beispiel genutzte Text-Format stellt dabei so eine Art Standard da, der auch in der Robot IDE genutzt wird. Daneben gibt es noch ein HTML-Format und auch die Möglichkeit Tests im BDD-Stil zu schreiben. Dies würde jedoch den Rahmen dieses Artikels sprengen.

“If you don’t care about quality, you can meet any other requirement.” – Gerald M. Weinberg

Abbildung 1 Das Beispiel aus Listing 1 in der Robot IDE

Abbildung 1 Das Beispiel aus Listing 1 in der Robot IDE

Das Beispiel zeigt die Verwendung eigener Keywords und wie diese wiederum in Testfällen zusammen mit weiteren Keywords kombiniert werden können. Ein Testfall ist dann erfolgreich, wenn jedes aufgerufene Keyword fehlerfrei durchgelaufen ist. Durch die Möglichkeit in Keywords wieder weitere Keywords aufzurufen ergibt sich ein einfaches und gleichzeitig mächtiges Konzept für das Schreiben von Tests und Test-Bibliotheken. Mit Hilfe der Robot IDE ist es so auch weniger technisch orientierten Projektmitgliedern möglich Test-Funktionalitäten zu schreiben und zu erweitern. Abbildung 1 zeigt das Beispiel aus Listing 1 in der Robot IDE.

Eine weitere Stärke des Robot Frameworks ist das Reporting. Für jeden Testlauf wird ein Report erzeugt, der die Ergebnisse übersichtlich auflistet und anhand der Hintergrundfarbe (grün oder rot) auch direkt den Erfolg (oder eben Misserfolg) dokumentiert. Aus dem Report, welcher als HTML-Seite vorliegt, ist es direkt möglich in eine Log-Seite (ebenfalls im HTML-Format) zu springen. Hier werden alle einzelnen Keyword-Aufrufe protokolliert. Dies erleichtert eine Fehlersuche bei der Erstellung der Tests und kann ggf. auch genutzt werden um weitere Testinformationen auszugeben in einer Art Debug-Ausgabe.

Abbildung 2 Beispiel-Reports

Abbildung 2 Beispiel-Reports

Die obige Abbildung zeigt Beispiele für Reports die vom Robot Framework erzeugt werden.

Das Robot Framework im Überblick

Nach dem Einstieg anhand eines einfachen Beispiels im vorherigen Kapitel gibt dieses Kapitel einen Überblick über die wichtigsten Aspekte des Robot Frameworks. Abbildung 3 skizziert diese Zusammenhänge .

Abbildung 3 Robot Framework im Überblick

Abbildung 3 Robot Framework im Überblick

Neben der Möglichkeit frei verfügbare Test-Bibliotheken zu nutzen oder eigene Keywords in Resource Files zu bündeln ist es natürlich auch möglich eigene Test-Bibliotheken zu programmieren. Auch wenn andere Programmiersprachen hier möglich sind passiert dies doch im Allgemeinen in Python oder Java.

Hiermit kommen wir dann auch direkt zum Installation Stack des Robot Frameworks (siehe Abbildung 3). Ursprünglich in Python programmiert ist die Unterstützung für Java im Laufe der Zeit immer besser geworden.

Abbildung 4 Robot Framework Installation Stacks

Abbildung 4 Robot Framework Installation Stacks

Im Prinzip kann man die Installation Stacks aus Abbildung 4 als eine Art Evolution betrachten. Nach der Anfangs reinen Python-Installation kam später mit der Integration von Jython die Möglichkeit hinzu, Keyword-Bibliotheken auch in Java zu programmieren. Mittlerweile ist es möglich das Robot Framework inkl. aller benötigten Bibliotheken (Python und Java) in JAR-Dateien zu installieren und zu nutzen. Dies hat oft den großen Vorteil, dass nicht alle Team-Mitglieder lokale Python- und Jython-Installationen benötigen. Java ist meist auf jedem Rechner installiert. Desweiteren lässt sich das Robot Framework in dieser Form auch schneller und einfacher für verschiedene Benutzer installieren.

Entwicklung eigener Keyword-Bibliotheken

Früher oder später reicht die Kombination bestehender Keywords zu neuen Keywords in den Resource Dateien nicht mehr aus und es müssen projekt-spezifische Keyword-Bibliotheken her. Die Anwendungsfälle hierfür sind vielfältig. Oft macht es Sinn z.B. eine Test-Möglichkeit für die Service-Schicht der zu testenden Anwendung zu schaffen. In einem Projekt haben wir eine Bibliothek für die Ansteuerung von Java JMX-Methoden geschrieben. Wobei diese im Prinzip schon wieder eine generische Test-Bibliothek sein könnte.

public class DatabaseLibrary {
	public static final String ROBOT_LIBRARY_SCOPE = "GLOBAL";
 
	private Connection connection = null;
 
	public void connectToDatabase(String driverClassName,
            String connectString,
            String dbUser,
            String dbPassword) throws SQLException,
                                      InstantiationException,
                                      IllegalAccessException,
                                      ClassNotFoundException {
 
		Class.forName(driverClassName).newInstance();
		setConnection(DriverManager.getConnection(connectString,
                                                         dbUser,
                                                         dbPassword));
	}
 
	public void disconnectFromDatabase() throws SQLException {
		getConnection().close();
	}
 
	public void tableMustExist(String tableName) throws
            SQLException,
            DatabaseLibraryException {
 
		DatabaseMetaData dbm = getConnection().getMetaData();
		ResultSet rs = dbm.getTables(null, null, tableName, null);
		try {
			if (!rs.next()) {
				throw new DatabaseLibraryException("Table: "
                                              + tableName
						+ " was not found");
			}
		} finally {
			rs.close();
		}
	}
 
…

Listing 2 – Ausschnitt aus der Datenbank Keyword-Bibliothek

Wie bereits erwähnt können Keyword-Bibliotheken für das Robot Framework in verschiedenen Programmiersprachen geschrieben werden. Listing 2 zeigt einen Ausschnitt aus der Datenbank-Bibliothek.

Im Prinzip ist die Vorgehensweise sehr einfach. Die öffentlichen Methoden einer Java-Klasse werden zu Keywords der entsprechenden Keyword-Bibliothek. Idealerwiese baut man daraus ein JAR, welches dann beim Starten des Robot Frameworks mit eingebunden wird. Für eine bessere Lesbarkeit erlaubt das Robot Framework das Einfügen von Leerzeichen und beliebige Groß- und Kleinschreibung beim Aufruf von Keywords (=Methoden) aus einer Java Bibliothek.

*** Setting ***
Suite Setup       Connect To Database    com.mysql.jdbc.Driver
                  jdbc:mysql://localhost:3333/databaselibrarydemo    dblib    dblib
Suite Teardown    Disconnect From Database
Library           org.robot.database.keywords.DatabaseLibrary

*** Test Case ***
Basic Checks
    Table Must Exist    DemoTable

Listing 3 – Einbindung eigener Java Keyword-Bibliotheken

Einbinden von Remote Bibliotheken

Eine oft sehr hilfreiche Funktion ist die Möglichkeit Test-Bibliotheken als sogenannte Remote Bibliotheken einzubinden. Bezogen auf unser Beispiel aus Listing 3 würde sich lediglich das Einbinden der Bibliothek mittels

Library org.robot.database.keywords.DatabaseLibrary

ädern. Der Aufruf würde dann wie folgt aussehen:

Library Remote http://localhost:8271

Dabei gehen wir davon aus, dass der Server für die Test-Bibliothek lokal unter dem Port 8271 läuft.

Remote Library Usage

Abbildung 5 Test Bibliotheken remote nutzen

Nicht jede verfügbare Test-Bibliothek unterstützt den Remote-Ansatz. Dies wird in der Beschreibung der entsprechenden Bibliothek dokumentiert.

“Testing is an infinite process of comparing the invisible to the ambiguous in order to avoid the unthinkable happening to the anonymous.” – James Bach

Die Remote-Unterstützung bietet einige Vorteile und sollte auch für Eigenentwicklung in Erwägung gezogen werden:

  • Gerade bei Java-Bibliotheken ermöglicht die Remote-Unterstützung, dass die eigentliche Robot-Installation allein mit Python (ohne Jython) lauffähig bleibt.
  • Es ist sehr einfach möglich testhalber Bibliotheken einzubinden auch ohne eine bestehende Installation sofort – ggf. auf mehreren Rechnern – erweitern zu müssen.
  • Abhängig vom Umfang der Tests kann dies auch der Lastverteilung dienen.

Der Einsatz von Remote Bibliotheken ist jedoch völlig optional und kann auch leicht zu einem späteren Zeitpunkt im Projekt erfolgen, falls notwendig. Die Auswirkungen auf bereits implementierte Tests sind – wie oben gesehen – minimal.

Architektur zum Testen von Web-Anwendungen

Ein häufiger Anwendungsfall für die Nutzung von Test-Frameworks ist sicherlich der automatisierte Test von Web-Anwendungen. Hier kommt es typischerweise zu einer Kombination aus der automatischen Ansteuerung eines Browsers, sowie dem Prüfen von Inhalten auf einer Datenbank (und ggf. weiteren angebundenen Systemen).

Abbildung 6 zeigt wie die Test-Architektur in einem solchen Fall mit dem Robot Framework aussehen könnte. Natürlich ist es jederzeit möglich, dass noch weitere Test-Bibliotheken genutzt werden.

Abbildung 6 Architektur zum Testen von Web-Anwendungen

Abbildung 6 Architektur zum Testen von Web-Anwendungen

Und auch wenn direkte Oberflächen-Tests oft notwendig sind, so sollte man deren Anzahl doch nach Möglichkeit begrenzen, denn oft laufen diese Tests nicht besonders stabil und müssen oft angepasst werden. Besser ist es hier nur einen Grundstock an Tests zu erstellen und nach Möglichkeit weitere Tests direkt gegen die Service-Schicht der Anwendung zu fahren.

Das Thema „Testen von Webanwendungen“ könnte problemlos einen kompletten eigenen Artikel füllen. Es soll hier daher nur ein sehr grober Überblick gegeben werden.

Fazit

Das Robot Framework ist ein leistungsstarkes und umfangreiches Test-Framework mit einer aktiven Community und einer Vielzahl zusätzlicher Tools. Auch gibt es für viele Technologien bereits fertige frei verfügbare Test-Bibliotheken.

Der Einstieg in die Test-Entwicklung mit dem Robot Framework ist nicht wirklich schwierig, erfordert aber eine gewisse Einarbeitungszeit um die verschiedenen Konzepte zu verstehen und sinnvoll einsetzen zu können. Hierbei helfen die wirklich sehr gute und umfangreiche Dokumentation, sowie das aktive Forum.

Letzen Endes macht es immer Sinn verschiedene Tools für den eigenen Anwendungsfall zu evaluieren. Dabei lohnt sich ein Blick auf das Robot Framework meiner Meinung nach auf jeden Fall.

The post Testautomatisierung mit dem Robot Framework appeared first on codecentric Blog.

Warum Lasttests auch für Entwickler interessant sind

$
0
0

Lasttests mit Hilfe von Tools wie JMeter oder Gatling sind ein gängiges Mittel um die Performance von Anwendungen zu analysieren und zu verbessern. In vielen Fällen stehen diese Tests am Ende der Entwicklung. Manchmal kommen sie erst dann zum Einsatz, wenn bereits erste Performance-Probleme im Live-Betrieb aufgetreten sind. Auch Monitoring und Fehlertoleranz sind Themen, die in der laufenden Entwicklung oft zu kurz kommen.

Viele Probleme ließen sich vermeiden, wenn bereits während der Entwicklung mehr Wert darauf gelegt wird, das Verhalten der Anwendung unter Last zu untersuchen. Das bedeutet auch, dass Lasttests nicht mehr nur in dedizierten, produktionsähnlichen Umgebungen ausgeführt werden, sondern auch in der lokalen Entwicklungsumgebung des einzelnen Entwicklers. Lasttests (und die zugehörigen Tools) sollten zum Handwerkszeug eines jeden Entwicklers gehören – genauso wie Unit-, Integrations- und Akzeptanztests.

Das hört sich komisch an, schließlich hat ein Lasttest auf einem kleinen Entwickler-Notebook wenig Aussagekraft. Es gibt aber eine ganze Reihe von Szenarien, in denen es gar nicht darum geht, die Leistungsfähigkeit des Systems unter realistischen Bedingungen zu analysieren. Oft geht es viel mehr darum, zu verstehen, wie sich einzelne Komponenten verhalten, wenn das ganze System unter Last gesetzt wird.

Im folgenden Beispiel wird mit Hilfe eines lokalen Lasttests, ein konkretes Problem untersucht: Wie reagiert meine Anwendung, wenn die Datenbank plötzlich nicht mehr erreichbar ist? Man kann natürlich argumentieren, dass die Konfiguration der Datenbankverbindung klar in die Verantwortlichkeit des Administrators fällt. Es gibt aber einige triftige Gründe, warum auch ein Entwickler verstehen muss, wie eine Datenbankverbindung sinnvoll konfiguriert wird und was bei fehlerhafter Konfiguration passieren kann:

  • Der Administrator kann nur das konfigurieren, was von außen konfigurierbar ist. Wenn aber der Datenbankverbindungspool innerhalb der Applikation (z.B. im Spring-Applikationskontext) definiert wird, ist es die Aufgabe des Entwicklers, die nötigen Optionen konfigurierbar zu machen.
  • Um herauszufinden, wie eigentlich „sinnvolle“ Einstellungen aussehen, ist der Administrator auf Rückmeldung von der Applikation angewiesen. Konkret heißt das: Log-Meldungen bei (möglichen) Problemen und Monitoring für wichtige Ressourcen (z.B. JMX-Anbindung für den Datenbankverbindungspool) müssen vom Entwickler bereitgestellt werden.
  • Angenommen der Administrator findet sinnvolle Werte für Timeouts. Damit ist die Sache nicht erledigt. Die Applikation muss mit Timeouts auch umgehen können. Exceptions müssen sinnvoll behandelt werden (Stichwort Logging und Exception-Mapping). Insbesondere im Fall von Timeouts sind die Exceptions zum Teil mehrfach geschachtelt und es ist gar nicht so einfach, an die nötigen Informationen zu kommen. Hier hilft nur eins: Selbst Hand anlegen, Lasttests durchführen, Fehler provozieren und Stacktraces studieren.

Beispielszenario – Datenbankausfall

An dieser Stelle könnte man sich auch ein ausgefeiltes Beispiel einer fehlertoleranten, verteilten Cloud-Anwendung überlegen. Doch so kompliziert muss es überhaupt nicht sein. Die meisten Anwendungen basieren auf einer Drei-Schichten-Architektur und sind damit sowieso schon „verteilt“, d.h. die Persistenzschicht (Datenbank) läuft unabhäng von der Anwendungslogik. Was passiert also, wenn in einer typischen Java Enterprise Webanwendung plötzlich die Datenbank ausfällt?

Klar ist zumindest, was passieren sollte: Die Webanwendung sollte in irgendeiner Weise dem Anwender mitteilen, dass im Moment keine Anfragen bearbeitet werden können (z.B. durch einen HTTP 5xx Fehler). Sobald die Datenbank wieder verfügbar ist, sollte die Anwendung wieder ganz normal funktionieren.

Test-Setup

Die verwendete Beispielanwendung ist so einfach wie möglich gehalten und basiert im wesentlichen auf der Spring Pet Clinic. Die Eckdaten der Webanwendung:

  • Spring und Spring-MVC
  • Tomcat 7 in Standardkonfiguration
  • Datenbank MySQL (Version: 5.5.22)
  • MySQL per JDBC angebunden (Version des Treibers: 5.1.22)
  • Connection-Pool DBCP in Standardkonfiguration

Der Lasttest wurde mit JMeter durchgeführt. Im konkreten Test geht es nicht darum, so viel Last wie möglich zu erzeugen. Vielmehr sollen möglichst gleichmäßig HTTP-Requests geschickt werden, damit später der Unterschied in den Antwortzeiten deutlich wird. Gemessen wird nur die Antwortzeit, unabhängig davon, ob der Request erfolgreich war oder nicht. Während der Test läuft, wird die Datenbankverbindung getrennt und anschließend wiederhergestellt. Die Eckdaten des JMeter-Tests:

  • 20 User Threads
  • Jeder Thread setzt pro Iteration einen HTTP GET-Request ab
  • Timeout für HTTP-Requests: 60 Sekunden
  • Wartezeit zwischen einzelnen Requests: eine Sekunde

Erster Testdurchlauf – Standardkonfiguration

JMeter Test - Antwortzeiten - ohne Timeouts

Solange die DB erreichbar ist, liegen die Antwortzeiten bei ca. 300 ms. Nachdem die Verbindung zur DB getrennt wurde, schnellen die Antwortzeiten auf über eine Minute hoch. Dass der Graph überhaupt noch lesbar ist (und die Spitze nicht in den Wolken verschwindet), liegt daran, dass im JMeter Test ein Client-Timeout von 60 Sekunden gesetzt ist. Tatsächlich antwortet die Anwendung überhaupt nicht mehr!

Um zu erfahren, warum das so ist, hat man als Entwickler viele Möglichkeiten: Von Basic-Tools wie Java VisualVM über Profiler bis hin zum Debugger hat man Zugriff auf alles, was einem das Java-Ökosystem zur Verfügung stellt. Ein Administrator hat an dieser Stelle weit weniger Optionen. Meistens bleibt nur der Blick ins Logfile (und dort herrscht in diesem Fall gähnende Leere). Dieser Problematik sollte man sich als Entwickler bewusst sein. Deshalb ist es wichtig, Mechanismen einzubauen um bei möglichen Problemen entsprechendes Feedback zu liefern. Eine Anwendung die sich einfach nur tot stellt, ist sehr schwer zu administrieren.

Im konkreten Fall hilft es, sich einen Thread Dump zu holen (z.B. mit VisualVM). Dort wird man feststellen, dass alle Threads warten. Manche warten auf Antwort von der Datenbank. Andere warten darauf, dass sie vom Connection-Pool eine DB-Verbindung zugewiesen bekommen. Und alle warten ohne Timeout, also potentiell ewig!

Zweiter Testdurchlauf – Angepasste Konfiguration

Sehen wir uns an, wie es besser funktionieren kann. Im folgenden Test wurde der ConnnectTimeout des MySQL-Treibers auf 3 Sekunden und der SocketTimeout auf 10 Sekunden gesetzt. Im Graphen kann man gut erkennen, wie nach dem Datenbankausfall zuerst der Socket-Timeout für bestehende Verbindungen greift – die Antwortzeit liegt kurzzeitig bei ca. 10 Sekunden. Anschließend wird bei jedem Request versucht eine neue Verbindung zur Datenbank aufzubauen. Diese Anfragen werden nach jeweils 3 Sekunden mit einer entsprechenden Fehlermeldung quittiert. In jedem Fall antwortet die Anwendung weiterhin. Auch im Logfile tauchen jetzt entsprechende Meldungen (inklusive Stacktraces) auf und weisen auf die kaputte Datenbankverbindung hin.

JMeter Test - Antwortzeiten - mit Timeouts

Der nächste Schritt wäre nun, sich Konfigurationsparameter des Verbindungspools anzusehen (in diesem Fall DBCP). Man kann auch andere Verbindungspools ausprobieren. Die JMX-Anbindung der verschiedenen Pools sind ebenfalls einen Blick wert. Durch dieses “Herumprobieren” erfährt man nicht nur einiges über die verwendeten Technologien. Jede Anpassung bringt auch Vorteile hinsichtlich Performance, Fehlertoleranz und Monitoring. Alles unterstützt durch einen sehr simplen Lasttest.

Fazit

JMeter und Konsorten sind nützliche Tools, die leider viel zu selten während der Entwicklung eingesetzt werden. Ihr Einsatz bietet den Entwicklern tieferes Verständnis in die Internas der Systeme und rückt oft vernachlässigte Themen in den Vordergrund. Daneben gibt es auch eine Reihe sehr konkreter Anwendungsfälle, in denen Lasttests eine große Hilfe darstellen:

  • Unterstützung bei der Auswahl von Bibliotheken (z.B. Connection-Pool, Cache, …)
  • Finden von Schwachstellen im Monitoring und Logging
  • Testen von Mechanismen für Fehlertoleranz (z.B. Circuit Breaker, Fail Fast Guard, …)
  • Identifizieren möglicher Performance-Probleme (z.B. durch Analyse der Zugriffe auf Fremdsysteme)

Ein Prinzip der agilen Softwareentwicklung ist, Probleme durch kontinuierliches Testen so früh wie möglich zu identifizieren und zu beheben. Das gilt für Monitoring, Fehlertoleranz und Performance ebenso wie für funktionale Anforderungen. Deshalb sollten Lasttests zum Repertoire eines jeden guten Entwicklers gehören.

The post Warum Lasttests auch für Entwickler interessant sind appeared first on codecentric Blog.

JMeter Tests mit Maven und Jenkins automatisieren

$
0
0

Mein letzter Post war ein Appell an Entwickler, öfter auf Lasttests zurückzugreifen, um bereits während der Entwicklung mögliche (Performance-)Probleme in der Anwendung zu identifizieren. Die Hürde dabei ist oft nicht so sehr das mangelnde Interesse, sondern der Aufwand, den man betreiben muss, um einen Lasttest durchzuführen. In meinem letzten Projekt lagen zum Beispiel zwei sehr ausführliche JMeter Tests im Source-Code-Repository. Daneben lag eine etwas längliche README-Datei, in der beschrieben wurde, was alles nötig ist, um den Test durchzuführen. Von der Installation von JMeter (ok, das muss sein) über nötige Plugins und zusätzliche JARs (für den JUnit-Sampler) bis hin zu den nötigen Anpassungen der Konfiguration war ich eine gute Stunde beschäftigt. Von Versionsproblemen und dem Setup der Testdatenbank ganz zu schweigen.

Als Entwickler ist man es gewohnt, zu automatisieren, wo es nur geht. Das Lasttest-Tool JMeter ist hierbei keine Ausnahme. Idealerweise sollten JMeter Tests auf Knopfdruck ausgeführt werden können. Und das in jeder Umgebung: Lokal auf dem Entwickler-Laptop, vom Jenkins gegen ein Testsystem oder als ausgewachsener Lasttest mit verteilten Agenten gegen ein produktionsähnliches Staging-System. Am einfachsten lässt sich das erreichen, wenn man JMeter in den Maven-Build integriert. Wie das aussehen kann, werde ich in den nächsten Abschnitten zeigen.

Integration von JMeter in den Maven-Build

Für die Integration von JMeter in den Maven-Build gibt es zwei Plugins (zumindest habe ich nur diese beiden Plugins gefunden): jmeter-maven-plugin und chronos-maven-plugin. Bei der Wahl des Plugins waren mir die folgenden Anforderungen wichtig (das kann natürlich von Projekt zu Projekt unterschiedlich sein):

  1. Das Plugin soll nicht von einer lokalen JMeter-Installation abhängig sein. Wenn nötig muss JMeter automatisch heruntergeladen werden (woher auch immer).
  2. Es muss möglich sein, Tests von der Kommandozeile ohne GUI zu starten und die Testergebnisse an einem vordefinierten Ort abzulegen.
  3. Es muss ebenfalls möglich sein, die JMeter-GUI aus Maven heraus zu starten.
  4. Die Verwendung von JMeter-Plugins sollte einfach sein.
  5. Es muss eine Möglichkeit geben, sinnvolle Reports zu generieren.

Die ersten drei Punkte werden von beiden Maven-Plugins erfüllt. Beim Reporting kommt es immer auf die konkreten Anforderungen an. Für das Chronos Plugin gibt es ein zusätzliches Reporting-Plugin, das die Ergebnisse des Tests in einen Maven-Report einbettet. Ich war vor allem auf der Suche nach aussagekräftigen Graphen, weshalb ich den Benefit des chronos-reporting-plugins nicht so hoch einschätzte. Der vierte Punkt war letztendlich ausschlaggebend dafür, dass meine meine Wahl auf das jmeter-maven-plugin fiel. Durch einfaches Hinzufügen einer Abhängigkeit zu kg.apc:jmeter-plugins waren die JMeter-Plugins sowohl im GUI wie auch im Headless Modus verfügbar. Diese Möglichkeit habe ich für das Chronos-Plugin nicht gefunden.

Die folgenden Code- und Konfigurationsschnipsel sind dem Beispiel-Projekt jmeter-maven-example entnommen. Für das Projekt existiert auch ein öffentlicher Jenkins, auf dem exemplarisch ein Job konfiguriert ist. Der Job erlaubt es, auf Knopfdruck JMeter Tests gegen eine echte (auf CloudBees gehostete) Anwendung durchzuführen. Die Konfiguration des Jenkins-Jobs wird weiter unten noch im Detail erklärt.

<plugin>
  <groupId>com.lazerycode.jmeter</groupId>
  <artifactId>jmeter-maven-plugin</artifactId>
  <version>1.8.1</version>
  <configuration>
    <!--
       Die Ergebnisse werden normalerweise in einer Datei 
       /target/jmeter/results/<TestName>-<TimeStamp>.jtl abgelegt. 
       Für die Weiterverarbeitung ist der Timestamp nur hinderlich.
    -->
    <testResultsTimestamp>false</testResultsTimestamp>
 
    <!--
       Für die Fehlersuche bewährt es sich anfangs das LogLevel hochzuschrauben.
       Die JMeter-Logs werden in die Datei jmeter.log geschrieben.
    -->
    <overrideRootLogLevel>DEBUG</overrideRootLogLevel>
 
    <!--
       Konsolen-Ausgaben des JMeter-Prozesses werden standardmäßig unterdrückt (warum auch 
       immer). Es wird aber explizit der Listener "Create Summary Results" verwendet, damit
       auf der Konsole der aktuelle Testfortschritt mitverfolgt werden kann.
    -->
    <suppressJMeterOutput>false</suppressJMeterOutput>
 
    <!--
       Wenn Tests fehlschlagen (z.B. HTTP-Requests in einen Timeout laufen), wird normalerweise
       auch das entsprechende Maven-Goal als fehlerhaft markiert (und nachfolgende Schritte nicht
       mehr ausgeführt). Im Beispiel sollen aber trotz Fehler Graphen erzeugt werden.
    -->
    <ignoreResultFailures>true</ignoreResultFailures>
  </configuration>
  <dependencies>
    <dependency>
      <groupId>kg.apc</groupId>
      <artifactId>jmeter-plugins</artifactId>
      <version>1.0.0</version>
      <exclusions>
         <!--
            Leider sind einige Abhängigkeiten nicht in mvncentral zu finden,
            deshalb müssen sie hier explizit ausgeschlossen werden.
            Für eine vollständge Liste, siehe https://github.com/mlex/jmeter-maven-example/
        -->
        <exclusion>
            <groupId>kg.apc</groupId>
            <artifactId>perfmon</artifactId>
        </exclusion>
        <!-- ... -->
 
        <!--
            Aufgrund eines Bugs im jmeter-maven-plugin (siehe 
            https://github.com/Ronnie76er/jmeter-maven-plugin/issues/77) müssen 
            JMeter-Abhängigkeiten auch ausgeschlossen werden.
        -->
        <exclusion>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>jorphan</artifactId>
        </exclusion>
        <!-- ... -->
      </exclusions>
    </dependency>
  </dependencies>
</plugin>

Die JMeter Tests werden einfach im Verzeichnis /src/test/jmeter abgelegt. Durch mvn jmeter:gui kann man die JMeter GUI starten. Dort kann man die Tests bearbeiten oder direkt aus der GUI heraus ausführen. Durch mvn jmeter:jmeter werden die Tests ohne GUI ausgeführt. Für diese Art der Ausführung eignet sich übrigens der JMeter Listener Create Summary Results um kontinuierlich (durch einfache Konsolenausgaben) über den Fortschritt des Tests informiert zu werden.

Damit die Tests problemlos auf unterschiedlichen Umgebungen ausgeführt werden können, ist es sinnvoll, die Konfiguration für diese Umgebungen irgendwo zentral abzulegen, zum Beispiel in einem Maven-Profil. Im Beispiel-Projekt sind auf diese Weise zwei Konfigurationen (für lokale Ausführung, sowie für Ausführung auf dem Jenkins) hinterlegt. Die Maven-Properties können über die userProperties Option des JMeter Maven Plugins an JMeter übergeben werden. Im JMeter Test kann man auf die Einstellungen dann mit Hilfe der Funktion ${__P(propertyName)} zugreifen.

<profiles>
  <profile>
    <id>local</id>
    <properties>
      <performancetest.webservice.host>localhost</performancetest.webservice.host>
      <performancetest.webservice.port>8080</performancetest.webservice.port>
    </properties>
  </profile>
  <profile>
    <id>jenkins</id>
    <properties>
      <performancetest.webservice.host>my.test.system</performancetest.webservice.host>
      <performancetest.webservice.port>80</performancetest.webservice.port>
    </properties>
  </profile>
  <build>
    <plugins>
      <plugin>
        <groupId>com.lazerycode.jmeter</groupId>
        <artifactId>jmeter-maven-plugin</artifactId>
        <version>1.8.1</version>
        <configuration>
          <propertiesUser>
            <webservice.host>${performancetest.webservice.host}</webservice.host>
            <webservice.port>${performancetest.webservice.port}</webservice.port>
          </propertiesUser>
        </configuration>
      </plugin>
    </plugins>
  </build>
</plugin>

JMeter Tests aus Jenkins heraus starten

Nachdem man bereits unterschiedliche Maven-Profile angelegt hat, ist die Konfiguration eines entsprechenden Jenkins-Jobs nicht mehr schwer. Interessant ist noch die Möglichkeit, auch den Umfang der Lasttests (d.h. die Anzahl der Threads und der Iterationen) über Maven-Properties festzulegen. Kombiniert mit einem parametrisierbaren Jenkins-Job erhält man so die Möglichkeit auf Knopfdruck unterschiedliche dimensionierte Lasttests zu starten.

Jenkins Job für JMeter Test

Reporting

Wie bereits erwähnt, kann man sich darüber streiten, wie “sinnvolles” Reporting aussieht. Wie der Berater so schön sagt: “It depends”. Für regelmäßig durchgeführte, immer gleich dimensionierte Performance-Tests eignet sich das Performance Plugin für Jenkins. In meinem Fall war ich hauptsächlich an Graphen interessiert, die das Systemverhalten im zeitlichen Verlauf des Tests wiederspiegeln. Diese Graphen können auch ohne GUI mit dem CMDRunner der JMeter-Plugins erstellt werden. Mit einigem Verbiegen und dem exec-maven-plugin war es auch möglich, die Erstellung der Graphen in den Maven-Build zu integrieren. Die nötige Konfiguration sieht nicht sehr schön aus, deshalb beschränke ich mich an dieser Stelle auf einen Link zum Beispielprojekt. Ein Maven-Plugin, das die Erstellung der Graphen für JMeter Tests wesentlich vereinfacht, ist bereits in Arbeit: jmeter-graph-maven-plugin.

Ergebnis

Am besten lässt man Bilder sprechen. Vom Start …
start-jmeter-job
… über die Dimensionierung …
size-jmeter-job
… und den Testdurchlauf …
watch-jmeter-job
… bis zum übersichtlichen und leicht verständlichen Ergebnis …
jmeter-job-result
… sind es nur wenige Schritte. So einfach können Lasttests sein.

The post JMeter Tests mit Maven und Jenkins automatisieren appeared first on codecentric Blog.

Leichtgewichtige virtuelle Maschinen mit Docker oder wie man 100 VMs laufen lässt

$
0
0

Virtuelle Maschinen haben viele Vorteile. Sie nutzen die Hardware besser aus, können leicht gesichert sowie ausgetauscht werden und isolieren einzelne Dienste voneinander. Sie haben jedoch auch Nachteile. Images sind sperrig und virtuelle Maschinen verbrauchen recht viele Ressourcen, da sie Hardware emulieren und ein gesamtes Betriebssystem laufen lassen. Mit Linux Containern gibt es eine leichtgewichtige Alternative. In diesem Artikel stelle ich Docker vor, ein Tool, das die praktische Anwendung von Linux Containern erleichtert. Dazu zeige ich anhand eines Beispiels, wie man 100 virtuelle Maschinen auf seinem System laufen lassen kann. Das Beispiel liegt dazu bei Github, so dass man es selbst ausprobieren kann.

Was ist Docker?

Linux Container (LXC) sind seit Version 2.6.24 Bestandteil von Linux and erlauben sogenannte System-Level Virtualisierungen. Sie setzen auf Linux cgroup und name spaces, die Prozesse voneinander isolieren. Sie laufen daher scheinbar auf ihrem eigenen System. Docker baut auf Linux Containern auf und besteht aus drei Teilen: Docker Daemon, Docker Images, the Docker Repositorys.

Docker Daemon läuft als root und verwaltet alle laufenden Container. So wie Virtuelle Maschinen auf Images basieren, basieren Docker Container auf Docker Images. Diese Images sind winzig verglichen mit virtuellen Maschinen Images und sind dank AUFS ”stapelbar”, so dass nur Änderungen gespeichert werden — siehe unten. Mit Hilfe von privaten und öffentlichen Docker Repositorys können Docker Images mit anderen geteilt und wie Source Code versioniert werden.

Ein Beispiel: 100 virtuelle Maschinen auf einem System

Um in die Nutzung von Docker einzuführen, zeige ich im Folgenden anhand eines Beispiels, wie Docker funktioniert. In diesem Beispiel werden 100 Docker Container jeweils mit eigener IP-Adresse gestartet und beantworten Webanfragen auf Port 8080. Als Webserver dient ein kleines Python Script. Das vollständige Beispiel ist bei Github verfügbar, so dass man es selbst ausprobieren kann.

Vom Dockerfile zum Docker Image

Ein Dockerfile beschreibt wie ein Docker Image erstellt werden soll. Für das Beispiel nutze ich zwei Dockerfiles, eins für die Python-Laufzeitumgebung und ein zweites für den eigentlichen Python Webserver. Listings 1 und 2 zeigen die beiden Dockerfiles.

FROM ubuntu:quantal
MAINTAINER Lukas Pustina <lukas.pustina@centerdevice.com>
RUN apt-get install -y python
Listing 1

Das FROM Kommando gibt das Ausgangsimage für das neue Image an. Hier wird ein öffentliches Ubuntu Image benutzt. Images werden zuerst lokal und dann im öffentlichen Docker Repository nachgeschlagen. Das RUN Kommando spezifiziert Shell-Kommandos, die während des Builds ausgeführt werden sollen. Hier wird Python installiert.

FROM docker-demo/python
MAINTAINER Lukas Pustina <lukas.pustina@centerdevice.com>
ADD webserver.py /opt/webserver/webserver.py
ADD run.sh /opt/webserver/run.sh
EXPOSE 8080
VOLUME ["/logs”]
Listing 2

Das zweite Docker Image leitet vom ersten Image mit der Python Laufzeitumgebung ab und fügt die beiden Dateien webserver.py and run.sh hinzu. Im Allgemeinen dürfen Docker Container nicht mit der Außenwelt kommunizieren — eine Deny All Regel. Falls Kommunikation möglich sein soll, muss diese explizit mit dem EXPOSE Kommando erlaubt werden. Erst dann wird der Port 8080 nach außen weitergeleitet. Das VOLUME Kommando gibt einen Mount Point an, an den Dateisysteme aus dem Host gebunden werden können. Auf diese Weise können global wiederverwendbare und teilbare Mount Points eingehängt werden.

Images bauen: docker build

Docker Images werden aus Dockerfiles erzeugt. Dabei erzeugt jedes Kommando eines Dockerfiles ein neues Image, das wie ein git Commit über eine eindeutige ID referenziert werden kann. Zum Beispiel erzeugt folgender Befehl das Image für die Python-Laufzeitumgebung

> docker build -rm -t docker-demo/python python

im Verzeichnis python, löscht dann alle Teil-Images (-rm) und taggt das Ergebnis-Image mit  ”docker-demo/python”.

> docker images

zeigt all zur Zeit existierenden Images an. In unserem Beispiel (Die Größe bezeichnet die virtuelle Größe — siehe unten):

docker-demo/python      latest   ef489f0186e1        Less than a second ago   210.7 MB
docker-demo/webserver   latest   b898a85622e4        Less than a second ago   210.7 MB
ubuntu                  quantal  b750fe79269d        9 months ago             175.3 MB

> docker images -tree

zeigt die Vererbungshierarchie der Images sowie ihre physikalische und virtuelle Größe an:

└─27cf78414709 Size: 175.3 MB (virtual 175.3 MB)
  └─b750fe79269d Size: 77 B (virtual 175.3 MB) Tags: ubuntu:quantal
    └─7c1926658b21 Size: 0 B (virtual 175.3 MB)
      └─ef489f0186e1 Size: 35.43 MB (virtual 210.7 MB) Tags: docker-demo/python:latest
        └─90807ce05b64 Size: 0 B (virtual 210.7 MB)
          └─d3822b811f4c Size: 0 B (virtual 210.7 MB)
            └─380871b22bde Size: 1.454 kB (virtual 210.7 MB)
              └─8f5a9cde5ae2 Size: 161 B (virtual 210.7 MB)
                └─b898a85622e4 Size: 0 B (virtual 210.7 MB) Tags: docker-demo/webserver:latest

Die physikalische Größe ändert sich also nur, wenn Dateien hinzugefügt werden. Das Tagging ändert die Größe nicht. Beachtenswert ist, dass ein Docker Image mit einem Python Webserver nur ca. 180 MB groß ist.

Änderungen machen

Wenn man eine Änderung an einem Image durchführt, so werden nur die entsprechenden Container geändert. In diesem Beispiel wird der Webserver Container ein zweites Mal gebaut und docker images -tree erneut aufgerufen:

└─27cf78414709 Size: 175.3 MB (virtual 175.3 MB)
  └─b750fe79269d Size: 77 B (virtual 175.3 MB) Tags: ubuntu:quantal
    └─7c1926658b21 Size: 0 B (virtual 175.3 MB)
      └─ef489f0186e1 Size: 35.43 MB (virtual 210.7 MB) Tags: docker-demo/python:latest
        └─90807ce05b64 Size: 0 B (virtual 210.7 MB)
          |─0e153dd3c547 Size: 0 B (virtual 210.7 MB)
          | └─922d6e34d2d5 Size: 1.454 kB (virtual 210.7 MB)
          |   └─4bfc3d0fb5b2 Size: 161 B (virtual 210.7 MB)
          |     └─abac38e20f27 Size: 0 B (virtual 210.7 MB) Tags: docker-demo/webserver:latest
          └─d3822b811f4c Size: 0 B (virtual 210.7 MB)
            └─380871b22bde Size: 1.454 kB (virtual 210.7 MB)
              └─8f5a9cde5ae2 Size: 161 B (virtual 210.7 MB)
                └─b898a85622e4 Size: 0 B (virtual 210.7 MB)

Die Basis-Images bleiben gleich und nur die Kommandos des zweiten Dockerfiles werden erneut ausgeführt. Sie ändern nur wenige Bytes; nicht schlecht für ein Image einer virtuellen Maschine.

Container starten: docker run

Um Docker Container eines bestimmten Images zu starten ruft man docker run auf:

> docker run -d -cidfile=webserver.cid -name webserver -v `pwd`/logs:/logs docker-demo/webserver:latest /opt/webserver/run.sh

In unserem Beispiel wird das Docker Image das zuletzt mit ”docker-demo/webserver” getagt worden ist gestartet. Das heißt, dass Image abac38e20f27 an Stelle von b898a85622e4 genutzt wird. Die Container-ID wird in die Datei webserver.cid (-cidfile) geschrieben, der Container “webserver” benannt (-name), und das lokale Verzeichnis logs wird nach /logs im Container gemappt. (-v; siehe Dockerfile oben). Der Prozess, der im Container ausgeführt werden soll, ist /opt/webserver/run.sh, welcher im zweiten Dockerfile in das Image kopiert worden ist. Man kann diesen Befehl auch mehrmals ausführen:

> for i in `seq 1 10`; do docker run -d -cidfile=webserver-$i.cid -name webserver-$i -v `pwd`/logs:/logs docker-demo/webserver:latest /opt/webserver/run.sh /logs; done

Das vollständige Beispiel auf Github startet ohne Probleme 100 Maschinen in einer Vagrant Box.

Anzeigen der laufenden Container: docker ps

Ähnlich wie ps zeigt docker ps die laufenden Container:

CONTAINER ID  IMAGE                          COMMAND                PORTS     NAMES
63120c069662  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-10
5af293d56a0b  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-9
a4a44f159658  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-8
40b35ff10aa2  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-7
6e9127f8a113  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-6
4623905191d2  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-5
e57def12fe25  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-4
c352eccb21de  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-3
f65e43115864  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-2
111c936c2763  docker-demo/webserver:latest   /opt/webserver/run.s   8080/tcp  webserver-1

Wie man sehen laufen 10 Container mit den Namen webserver-* und lauschen auf Port 8080.

Informationen anzeigen lassen: docker inspect

docker inspect gibt zusätzliche Informationen über Container als JSON Dokument zurück. Dies beinhaltet u.a. den hostname, das Verzeichnis des Images oder auch die IP-Adresse, nach der im Folgenden gegrept wird:

> docker ps -q | xargs docker inspect | grep IPAddress | cut -d ‘”‘ -f 4

172.17.0.12
172.17.0.11
172.17.0.10
172.17.0.9
172.17.0.8
172.17.0.7
172.17.0.6
172.17.0.5
172.17.0.4
172.17.0.3

Jeder Container hat also ein eigenes Netzwerkinterface mit eigener IP-Adresse. Zusammen bilden sie ein eigenes Netzwerk von isolierten, System-Level virtuellen Maschinen.

Ressourcenverbrauch

ps aux | grep ‘lxc-start\|python’” zeigt die verbrauchten Ressourcen der 10 Linux Container an. Man kann sehen, dass pro Container ca. 1,3 MB und pro Python Instanz ca. 7 MB RAM verbraucht werden — ganz schön beeindruckend im Vergleich zu klassischen virtuellen Maschinen.

  PID %CPU %MEM   RSS COMMAND
12197  0.0  0.2  1316 lxc-start
12249  0.0  0.2  1320 lxc-start
12291  0.0  1.3  6968 /usr/bin/python /opt/webserver/webserver.py
12296  0.0  0.2  1320 lxc-start
12346  0.0  1.3  6960 /usr/bin/python /opt/webserver/webserver.py
12348  0.0  0.2  1316 lxc-start
12399  0.0  1.3  6964 /usr/bin/python /opt/webserver/webserver.py
12400  0.0  0.2  1316 lxc-start
12450  0.0  0.2  1316 lxc-start
12496  0.0  1.3  6964 /usr/bin/python /opt/webserver/webserver.py
12497  0.0  0.2  1316 lxc-start
12546  0.0  0.2  1316 lxc-start
12594  0.0  0.2  1320 lxc-start
12648  0.0  1.3  6968 /usr/bin/python /opt/webserver/webserver.py
12649  0.0  0.2  1316 lxc-start
12700  0.0  1.3  6968 /usr/bin/python /opt/webserver/webserver.py
12701  0.0  1.3  6968 /usr/bin/python /opt/webserver/webserver.py
12707  0.0  1.3  6960 /usr/bin/python /opt/webserver/webserver.py
12711  0.0  1.3  6968 /usr/bin/python /opt/webserver/webserver.py
12712  0.0  1.3  6964 /usr/bin/python /opt/webserver/webserver.py

docker stop, kill, remove

Um Container zu stoppen, genügt es, dem ausgeführten Prozess ein SIGTEM oder SIGKILL zu schicken. docker stop versucht zunächst ein SIGTERM zu schicken. Bleibt dies bis zu einem Timeout ohne Reaktion, wird ein SIGKILL geschickt. docker kill sendet sofort ein SIGKILL. Wie bei klassischen virtuellen Maschinen bleiben die Images nach Beenden der VM bestehen. docker rm löscht Container.

Es gibt mehr

Linux Container bieten durch Docker noch viele weitere Möglichkeiten, als ich hier beschrieben habe. So ist es möglich, Volumes zwischen Containern zu teilen. Container können auch multi-homed sein und mehrere Netzwerkinterfaces konfigurieren. Private Repositorys können leicht aufgesetzt werden und ermöglichen die Verwaltung eigener Images. Zusätzlich können weitere der Kommandozeilen-Argumente von Docker in Dockerfiles eingetragen werden.

Docker bei CenterDevice

Bei CenterDevice haben wir unsere Entwicklungsumgebung auf eine Vagrant-basierte Virtual Box migriert, die Docker Images für all unsere Backenddienste wie MongoDB, RabbitMQ und Tomcat nutzen. Auf diese Weise können Entwickler ihre Entwicklungsumgebung leicht aufsetzen sowie die Dienste isoliert und wie auf Produktion laufen lassen, ohne die Ressourcen ihres Entwicklungssystem zu überlasten.

In folgenden Artikeln werde ich zeigen, wie das Erzeugen von Docker Images in die Continuous Integration Pipeline eingebaut werden kann. So können erfolgreiche Builds automatisch in Docker Images überführt werden, die dann in der Entwicklung, zum Testen und sogar in der Produktion eingesetzt werden können.

Ich werde über dieses Thema auch in Zukunft bloggen. Also bleibt dran und meldet Euch bei mir, falls Ihr Fragen oder Anregungen habt.

The post Leichtgewichtige virtuelle Maschinen mit Docker oder wie man 100 VMs laufen lässt appeared first on codecentric Blog.

Viewing all 129 articles
Browse latest View live




Latest Images