On Github thred / presentation-bdd
http://thred.github.io/presentation-bdd/
Vom Use-Case zum Test-Case
Behavior Driven Develoment ist eine Methode der agilen Softwareentwicklung und hat das Ziel die Zusammenarbeit der Beteiligten zu verbessern. Außerdem sollen aus dieser Zusammenarbeit automatisierte Tests als Benefit entstehen.
Hinweise zu dieser Präsentation:
T drücken um diesen Text zu verstecken bzw. zu zeigen.
C drücken um die Navigationshilfen zu verstecken bzw. zu zeigen.
Mit SPACE zur nächsten Folie wechseln.
Dieser Vortrag konzentriert sich auf folgende zwei Punkte:
Was ist BDD (Behavior-Driven Development) eigentlich?Software Entwickler, Porsche Informatik GmbH
Mein name ist Manfred Hantschel und ich bin Softwareentwickler bei der Porsche Informatik GmbH in Salzburg.
Meine Aufgaben reichen von der technischen Umsetzung und Leitung bis hin zur Produktverantwortung für mehrere Projekte. Die Palette der Applikationen reicht dabei von internationalen webbasierten Unternehmensportalen, über PDA- und Smartphone-basierten Applikationen für die Unterstützung von Händlern, bis hin zu Verwertungsplattformen für Neu- und Gebrauchtfahrzeuge.
Seit 2011 setze ich mich, unter Anderem, mit dem Thema Testautomatisierung und mit der Einführung von Behavior Driven Development auseinander. Die Methodik wird in der Porsche Informatik bei mehreren Projekten – teilweise Webapplikationen, teilweise Client-Server-Applikationen auf Java-Basis – eingesetzt.
Entstanden ist BDD ursprünglich aus dem Test-Driven Development. Es wurde zum ersten Mal 2006 von Dan North beschrieben (siehe "Introduction BDD" von Dan North und http://behaviourdriven.org/)
Er bemerkte, dass TDD oft nicht erfolgreich war. TDD wurde zwar umgesetzt, aber eher als Last empfunden, da TDD nicht in den Designprozess integriert wurde.
BDD versucht nun die Probleme zu lösen und geht dabei einen guten Schritt weiter.
In erster Linie soll die Zusammenarbeit zwischen
verbessert werden. Aus dieser Zusammenarbeit soll dann TDD als Benefit entstehen.
Behavior-Driven Development versucht die Prinzipien von Domain-Driven Design mit denen von Test-Driven Development zu vereinen.
→ next fragment Domain-Driven Design legt den Schwerpunkt der Softwareentwicklung auf die Fachlogik der Anwendung anstatt auf die technische Umsetzung. Die komplexen fachlichen Zusammenhänge werden zunächst in einer Geschäftslogikschicht abgebildet und erst dann werden die Teilbereiche umgesetzt.
In der Praxis bewährt sich der DDD Ansatz im höchsten Maße. Durch die Schichtenarchitektur generell wird die Problemlösung vereinfacht und die Arbeitsteilung erleichtert. Leider laufen Entwickler aber auch Gefahr die eigentliche Aufgabe aus den Augen zu verlieren, weil sie nur den eigenen Teilbereicht wirklich kennen. Jede einzelne Umsetzung ist toll, aber das Ganze hilft dem Benutzer nicht.
→ next fragment Test-Driven Development soll ja den Entwickler dazu anhalten einen evolutionären Prozess in der Entwicklung einzuhalten. Es soll den Entwickler dazu bringen, sich in erster Linie um das Problem selbst zu kümmern, und nicht nur um die Umsetzung einer Lösung. Dazu soll der Entwickler vor der Umsetzung zuerst Tests dazu schreiben.
In der Praxis ist das leider nicht so einfach. Unit-Tests fokussieren den Entwickler schon sehr stark auf die Umsetzung der Lösung. Konzentriert sich der Entwickler zuerst auf Unit-Tests wird er schon frühzeitig mit Problemen konfrontiert, die technische Entscheidungen fordern. Mit Integrations-Tests verhält es sich etwas besser, jedoch decken diese meist nur die Serviceschicht ab ohne auf Details der eigentlichen Aufgabenstellung einzugehen.
Und wie für Domain-Driven Design, gilt auch für Behavior-Driven Development: BDD ist nicht nur eine Technik, BDD ist eine Methode der Softwareentwicklung.
standardisierte Sprache für fachlichen Ablauf
User-Stories
Akzeptanzkriterien
Story: As a <ROLE> I want <FEATURE> in order to <BENEFIT>
Feature: Given <PRECONDITION> When <ACTION> Then <POSTCONDITION>
→ findet frühzeitig fachliche Fehler
Für das Domain-Driven Design stellt BDD eine Sprache zur Verfügung, mit der es möglich ist die fachlichen Abläufe detailliert und standardisiert zu formulieren. Mit Hilfe der Sprache lassen sich User-Stories verfassen und Akzeptanzkriterien definieren.
Die Sprache orientiert sich dabei am Benutzer selbst und ist für alle Beteiligten verständlich. Bei der Beschreibung wird gänzlich auf technische Feinheiten verzichtet, lediglich dem fachlichen Ablauf wird ein logischer Prozess zugrunde gelegt.
Und bereits da werden viele Probleme gelöst. Aus meiner Erfahrung tauchen hier oft schon die großen Probleme der Umsetzung auf – nämlich dann wenn der fachliche Prozess schon nicht logisch ausgereift ist. Die Entwicklung kann fast alles umsetzen, einzige Vorbedingung: die Aufgabe muss deterministisch sein. Für die Formulierung des Akzeptanz-Tests muss das Problem auf einem Level beleuchtet werden, das logische Probleme frühzeitig zu Tage fördert.
Feature: Sales list As a manager I want to list all employees in order to select and edit one. Scenario: Given the list of employees is displayed When I click on the name of an employee Then the detail page should be opened.
→ Schlüsselwörter
Konzentration auf das Problem
Umsetzung kann ignoriert werden
Eingehen auf fachliche Details
Für das Test-Driven Development stellt BDD ein Werkzeug zur Verfügung - nämlich Akzeptanz-Tests.
Akzeptanz-Tests konzentrieren sich, wie von TDD gefordert, auf das Problem selbst. Die Umsetzung der Lösung kann zunächst vollkommen ignoriert werden. Die Tests bewegen sich auf einer rein fachlichen Ebene, anders als Unit-Tests. Trotzdem ist es möglich, ja gefordert, auf fachliche Details einzugehen.
Durch die Schlüsselwörter ist es möglich die Akzeptanz-Tests automatisch zu verarbeiten und programmtechnisch Tests auf Grundlage der Kriterien verfassen.
Die Testquadranten von Brian Marick und in erweiterter Form von Lisa Crispin geben eine gute Orientierungshilfe in Bezug auf verschiedene Testarten.
Im Wesentlichen werden Tests nach 2 Ebenen kategorisiert:
Die Unterscheidung zwischen technisch und fachlich geprägt kann man am leichtesten treffen, wenn man sich das Zielpublikum vor Augen hält mit dem man über einen Test spricht bzw. sich Gedanken macht, wer denn einen Test durchführen könnte. In einem technisch geprägten Test werden die Durchführenden wohl eher Entwickler sein wogegen bei fachlich geprägten Tests ein Tester oder vielleicht der Product-Owner selbst der Durchführende sein können.
Man könnte auch sagen "vor der Entwicklung" und "nach der Entwicklung".
Entwickler die Erfahrungen mit Test-Driven-Development (TDD) gemacht haben, berichten oft, dass sie ihre Systeme/Klassen anders (einfacher) gestalten wenn die Tests vorher geschrieben werden – somit unterstützen diese Tests das Team während der Entwicklung. Außerdem werden durch die ständige Durchführung der Tests Seiteneffekte sofort entdeckt – eine weitere, nicht unwesentliche, Unterstützung.
Tester haben an und für sich einen destruktiven Job – sie versuchen das System zu brechen bzw. Fehler zu finden. Wenn ihnen das nicht gelingt, kann man mit hoher Wahrscheinlichkeit davon ausgehen, dass das System robust ist und die getesteten Funktionalitäten gewährleistet sind. Somit versuchen sie, das Produkt zu kritisieren und Schwachstellen aufzudecken.
Versucht man nun die verschiedensten Testarten in dieses System einzuordnen, fällt das relativ leicht:
Diese Arten von Tests – meist manuell durchgeführt – versuchen Fehler im System zu finden, kritisieren also das Produkt. Auf der zweiten Ebene betrachtet sind diese Tests aber ausschließlich durch fachliche Anforderungen geprägt – man testet also gegen die fachliche Spezifikation.
Wenn man Unittests & Co nach TDD schreibt, unterstützen sie das Team bei der Arbeit indem sie Einfluss auf die Systemarchitektur haben und Seiteneffekte sehr schnell erkannt werden. Allerdings sind sie technisch geprägt, werden also durch Entwickler entworfen und geschrieben.
Auch diese Tests versuchen das Produkt zu kritisieren indem sie die nicht-funktionalen Anforderungen in Frage stellen. Sie sind technisch geprägt da ein Product-Owner wohl kaum in der Lage sein würde solche Tests zu entwickeln.
Last but not least sind Vorgehensweisen wie Prototyping, Storyboards & Co sowohl fachlich geprägt als auch unterstützend für das Team. Eine (oft viel zu wenig beachtete) Grundregel in der Softwareentwicklung besagt: Je besser eine Anforderung vorbereitet und durchdacht ist, umso einfacher wird die Entwicklung. Hier ist auch das Behaviour-Driven-Development anzusiedeln!
Wie kann man sich nun vorstellen, dass BDD im Entwicklungsprozess umgesetzt werden kann?
Zunächst werden die Anforderungen textuell durch Fallbeispiele beschrieben.
genau ein Anwendungsfall
ideal aus Benutzersicht
User-Story: Add article to cart As a customer I want to select an article in order to put it into my shopping cart.
→ vermeidet Redundanz in Lasten- und Pflichenheft, Use-Cases und Konzepten
Fallbeispiele haben einen anderen Fokus als Use-Cases, Konzepte, Protokolle, Lasten- oder Pflichtenhefte: Fallbeispiele beschreiben genau einen Anwendungsfall aus der Sicht eines Benutzers. Wir schreiben Applikationen für Benutzer und darauf liegt der Fokus.
Klar, wir können auch Lasten- und Pflichtenhefte scheiben, aber die sind für Kunden. Wir können auch Use-Cases scheiben, aber die sind für Tester. Wir können auch Konzepte schreiben, aber die sind für Entwickler. Unser Ziel ist eine Sprache zu sprechen, die alle verstehen, und daher schreiben wir User-Stories.
Und weil wir agil entwickeln, brauchen wir die User-Stories sowieso, nämlich als Sprintziele. Aber erst durch BDD finden die User-Stories wirklich Einzug in unser Projekt, da sie maschinell verarbeitet werden müssen.
Als nächster Schritt werden die Fallbeispiele um Akzeptanzkriterien erweitert und automatisiert.
mehrere Szenarien je User-Story
Aktionen eines Benutzers
Sonder- und Problemfälle
Einfachheit ist Trumpf
Scenario: Add article to cart [...] Given the detail page of article "ART000042" is displayed When the user clicks the "Add to Cart" button Then the added to cart message should appear And the cart should contain the article "ART000042"
Zu jeder User-Story werden mehrere Akzeptanzkriterien verfasst. Diese Scenarios detaillieren die Story, beschreiben Sonder- und Problemfälle und listen mögliche Aktionen des Benutzers auf.
Auch diese User-Stories sind in einer Sprache verfasst, die alle Beteiligten lesen und verstehen können.
Die Texte basieren auf Schlüsselwörtern, im Normalfall "Given", "When" und "Then". Um den Text besser lesbar zu gestalten bieten manche Frameworks auch andere Schlüsselwörter wie z.B. "And" und "But" an. Ebenso bieten die Frameworks das Parsen von Variablen und Tabellen aus den Texten selbst an. Der restliche Text wird 1:1 an das Testframework übergeben.
Dadurch Einbindung in Entwicklungsprozess
Stories → Werkzeug
fachliche Fehler früh erkennen
Die fertigen User-Stories und Akzeptanzkriterien werden mit dem Kunden gemeinsam entwickelt, oder zumindest mit ihm abgestimmt. Der Kunde wird somit in den Entwicklungsprozess mit eingebunden. Hier zeigt sich schon ein weiterer Vorteil. Früher sind fachliche Fehler erst im fertigen Produkt aufgetaucht. Die Stories geben dem Entwicklungsteam ein Werkzeug in die Hand, mit dem der fachliche Ablauf vollständig definiert werden kann.
Wenn man wirklich agil arbeitet, können die Akzeptanzkriterien ein Lasten- und Pflichtenheft zumindest teilweise ersetzen.
Frühzeitig eingebunden
Szenarios als automatisierte Testfälle
Ergänzen Testkatalog (aber kein Ersatz!)
Bessere Kommunikation
Auch die Qualitätssicherung wird frühzeitig in den Prozess eingebunden. Schon das ist ein enormer Vorteil. Oft wird die Qualitätssicherung in Projekten stiefmütterlich behandelt. Die fertige Applikation wird übergeben und die Qualitätssicherung soll schauen wie sie damit zurecht kommt.
Da die Akzeptanzkriterien den Testkatalog ergänzen wird der Qualitätssicherung auch viel Arbeit abgenommen. GUI-Tests werden mit dem Produkt mitgeliefert und muss nicht nachträglich und umständlich definiert werden.
Implementation: Given, When, Then
Test-First
Eingebunden in Spezifikations- und Testprozess
Die Entwickler kümmern sich zunächst um die Umsetzung der Tests. Bevor die Applikation fertig ist, könnte sie theoretisch schon getestet werden. Auch hier zeigt sich der Vorteil, dass Entwickler schon frühzeitig in den Spezifikationsprozess eingebunden werden.
Erst jetzt folgt die Umsetzung der Anforderung.
Umsetzung nach User-Stories und Akzeptanz-Tests
QA testet zunächst manuell, dann automatisiert
Automatische Spezifikation für Kunden
Erst im dritten Schritt wird die Anforderung umgesetzt. Das Entwicklungsteam hat bereits automatische Tests an der Hand.
Die Qualitätssicherung führt die Test zunächst manuell durch, bis klar ist, dass die Szenarien den nötigen Testumfang abdecken. Danach können diese Tests jederzeit automatisch durchgeführt werden.
Als zusätzlicher Benefit kann aus den User-Stories auch automatisch eine Spezifikation der Applikation abgeleitet werden.
Dieser Vortrag konzentriert sich auf folgende zwei Punkte:
Was ist BDD (Behavior-Driven Development) eigentlich?1.) Product-Owner formuliert die Idee
2.) Anforderung wird in Datenbank erfasst
3.) Epic aus Anforderungen formulieren
4.) User-Stories für Epics
Am Anfang war die Idee. Diese wird meist vom Kunden bzw. dem Product-Owner ausgebrütet und formuliert. Die Idee kann aber auch von anderen Beteiligten kommen, z.B. von der Entwicklung. Die Idee wird bei uns in einer Anforderungsdatenbank, noch in Prosa, erfasst. Die Anforderung enthält eine grobe Vorstellung wie das Feature aussehen soll und beschreibt Hintergrundinformationen.
In der Porsche Informatik wird die Anforderungsdatenbank auch für die offizielle Statusverfolgung genutzt, da sich unsere Kunden mit dem agilen Vorgehen oft noch nicht richtig anfreunden können. Teilweise dient die Anforderungsdatenbank auch als Quelle für Aufwandsschätzungen, besonders wenn die Umsetzung für das nächste Wirtschaftsjahr geplant ist. Auch das deckt sich noch nicht mit dem agilen Vorgehen.
Für große Anforderungen, die nicht in einem Sprint umgesetzt werden können, werden jetzt User Epics verfasst. User Epics sind Sammlungen von User-Stories, es müssen aber zu Beginn nicht alle User-Stories ausformuliert sein.
Alle Beteiligten arbeiten mit
Einfache Fragen stellen
Es Entstehen: Behaviors, Story-Files, UI-Mockups, Aufwände, Schnittstellen, ...
Bevor das Feature umgesetzt wird, wird es noch konkretisiert. Dies geschieht durch Kunde, QA und Entwicklung gemeinsam.
Grundsätzlich werden dem Kunden "einfache Fragen" gestellt. Die Ergebnisse aus diesen Abstimmungen werden einem Story-File festgehalten, in Behaviors aufgeteilt und im Versionskontrollsystem eingepflegt. Behaviors entsprechen den Szenarien, müssen jedoch noch nicht so formuliert sein, dass sie maschinell verarbeitet werden können.
Je nach Umfang des Features entstehen auch noch zusätzliche Dokumentation: UI-Mockups, eine Aufwandsschätzung, Schnittstellendokumentationen, usw. Wir achten darauf, dass jede einzelne zusätzliche Dokumentation auch in der Story referenziert ist und die Redundanz so gering wie möglich ist.
Bewertung für Automatisierung: Wichtigkeit, Aufwand, Sinn
Formulierung der Szenarien
User-Story: Add Article to Cart As a customer I want to select an article in order to put it into my shopping cart. [...] Scenario: Duplicate Article Given the shopping cart already contains an article. When the user selects the same article And clicks the "Add to Cart" button Then the cart should contain the article only once But with a count of 2. [...]
Kurz vor der Sprintplanung wird zwischen Entwickler und Tester abgestimmt, welche Verhaltensweisen man formalisieren und automatisieren will.
Entscheidungskriterien dabei sind:
Auf Basis dieser Entscheidungen werden dann die Szenarien formalisiert (was rausfällt landet in einem Testkatalog).
Im Entwicklungszyklus durchwandert ein Story-File (jetzt Feature-File) dann mehrere Stationen. Diese sind als Verzeichnisse im Repository abgebildet. Grundsätzlich wird zwischen Stories unterschieden, die sich in Entwicklung befinden und jenen, die fertig umgesetzt wurden und Teil der Systemspezifikation werden.
Zunächst befindet sich ein Story-File in "preparation". Soll die Story im Sprint umgesetzt werden, wird das File in "current" verschoben. Wenn die Umsetzung erledigt wurde, wird das File in "finished" verschoben.
→ next fragment Nach dem Sprint wird das Story File in den "Feature-Tree" eingepflegt. Der "Feature-Tree" ist nach den Modulen im System aufgebaut. Die Übernahme erfolgt entweder durch kopieren der Datei oder durch händisches Anpassen von bestehen Story-Files.
In einem Projekt der Porsche Informatik wird der "Feature-Tree" sogar dazu verwendet, automatisch eine Systemspezifikation zu generieren. Dazu werden die Story-Files und zusätzliche Dateien im "Feature-Tree" zu einem Dokument zusammengefasst. Dieser Prozess ist leider sehr aufwendig, das Ergebnis ist jedoch beachtlich: Jede Version enthält eine getestete und exakte Systemspezifikation als PDF Dokument.
@Given("the user $name is logged in") public void ensureUserLoggedIn(String name) { User user = userService.getCurrentUser(); if (!name.equals(user.getName())) { userService.logout(); userService.login(name); user = userService.getCurrentUser(); } assert name.equals(user.getName()); }
Wie erfolgt nun die Anbindung der Szenarios an die Applikation?
Grundlegend ganz einfach: in Steps-Klassen werden die Texte aus den Szenarien implementiert. Das Beispiel zeigt wie das grundsätzlich funktionieren könnte. Doch in dieser Art und Weise läuft man schnell Gefahr, dass die Wartungsaufwände für die BDD-Tests zu hoch werden.
Daher haben wir in diesem Bereich viel Energie investiert um die Tests robust zu gestalten.
@Autowired private UserWorkflow userWorkflow; @Given("the user $name is logged in") public void ensureUserLoggedIn(String name) { User user = userWorkflow.login(name); assert name.equals(user.getName()); }
Grundsätzlich agieren die Steps nun auf Workflows. Erst die Workflows führen die Aktionen in der Applikation durch. Workflows agieren modulübergreifend, können daher auf die Schnittstellen der gesamten Applikation zugreifen. Die Logik wandert dadurch aus den Steps heraus und die BDD-Tests werden stabiler. Ähnliche Verhaltensweisen müssen auch nicht mehrfach implementiert werden.
Mit dieser Technik werden auch Testdaten aufgebaut. Die Tests müssen nicht auf einer vorbereiteten Datenbank laufen, sie erstellen die nötigen Testdaten selbst. Robustheit ist der Schlüssel.
Das Testinterface ermöglicht uns auch Aktionen auszuführen, die in der Applikation eigentlich nicht möglich wären, z.B. einen Benutzer zu löschen anstatt nur ein "gelöscht"-Flag zu setzen.
Bleibt aber immer noch das Problem die GUI zu steuern.
Um die GUI zu Steuern erzeugen wir für die Ansichten und Wartungen in der Applikation, Pages und Komponenten. Die Klassen beschreiben beispielsweise die Felder einer Login-Seite, also z.B. "Benutzername", "Passwort" und den "Anmelden" Button. Zentrale Komponenten, wie z.B. einen Suchdialog, werden in Component Klassen definiert, damit diese in mehreren Pages zur Verfügung stehen.
Die Pages interagieren über eine eigene Driver-API mit der GUI. In der API stehen die gängigsten Kommandos für die Automatisierung zur Verfügung, als z.B. "Klicke auf ein Element", "Fülle Feld X mit Y". Diese API soll uns ermöglichen auf Versionsupdates und Änderungen im Automatisierungstool zu reagieren.
Der Driver ist nun die spezielle Implementierung eines Automatisierungstools - in unserem Fall: Selenium 2.x (Webdriver). In den Driver ist auch eine Application Map integriert. Sollte das Mapping von z.B. Feld-Name auf Feld-Id nicht automatisch möglich sein, kann dieser Definition ein Mapping hinterlegt werden.
Die API und der Driver werden applikationsübergreifed eingesetzt.
Anpassungen nur an einer Stelle
Wiederverwendung der Workflows, Components und Pages
Mapping unterstützt Test-Driven-Development
Automatisierungstool ist austauschbar
Mit diesem scheinbar sehr komplexen Framework versuchen wir dem Wartungsproblem bei der Testautomatisierung in den Griff zu bekommen. Wenn man das Framework nochmal genau betrachtet, stellt man schnell fest, dass der Aufwand ein neues Scenario zu automatisieren darin besteht, die betroffenen Pages und deren Verbindung untereinander zu beschreiben.
Zudem haben viele Scenarios dieselben oder zumindest sehr ähnliche Vorbedingungen – es stellt sich also sehr schnell der Vorteil der Wiederverwendung ein.
Durch die Abstraktionsebenen und das automatische Mapping von abstrakten Feldbezeichnungen auf ID’s ist es nun auch problemlos möglich, GUI-Tests vorab zu automatisieren. Im Idealfall werden die Steps nun während der Entwicklung "grün”.
Nicht alles automatisieren
KISS
Robustheit, Robustheit, Robustheit
BDD ist mehr als automatisierte GUI-Tests
Je mehr man automatisiert, umso mehr muss man auch pflegen. Selbst wenn man ein ausgeklügeltes Framework verwendet, Anpassungen wird es immer geben. BDD kann sowieso keine erfahrene QA-Abteilung ersetzen, also sollten wir in erster Linie essentielle Teile der Applikation testen. Wir nennen das Positiv-Tests.
Verketten Sie nicht unzählige Vorbedingungen in einem Scenario nur weil es die Steps schon gibt. Verstecken Sie die Komplexität lieber in der Implementierung der Steps und formulieren Sie aussagekräftige Steps.
Je komplizierter der Weg vom Scenario bis zur fertigen Automatisierung ist, umso schwieriger wird es sein, ihre Entwickler zum Mitmachen zu bewegen.
... steht etwas im Widerspruch zur obigen Aussage. Wenn man aber Steps mit Record&Replay Scripten automatisiert, ist man schnell in der Wartungsfalle und kommt nur unglaublich schwer wieder heraus.
Achten Sie bei Tests auf ihre Robustheit. Kein Test geht von Vorbedingungen aus, die nicht spezifiziert sind. Will ich einen Artikel verkaufen, muss ein verkaufbarer Arktikel verfügbar sein. Das "Given" überprüft ob ein solcher Artikel vorhanden ist, legt wenn nötig einen an und merkt sich die Id, damit der Artikel im "When" verkauft werden kann. Stories sollten ihre Testdaten aufräumen, und wenn die eigenen Testdaten zu Beginn schon in der Datenbank sind, trotzdem laufen.
Die GUI-Tests, die anhand der Feature-Files ablaufen sind beeindruckend. Leider wird aber BDD oft auf dieses Feature reduziert. Wird weder der kommunikative Ansatz genützt noch das "Test-First"-Prinzip eingehalten kann man kaum mehr von BDD sprechen. Dann ist es den Aufwand auch nicht mehr wert die Feature-Files zu pflegen; es reicht mit Selenium GUI-Tests zu implementieren.
Wie fügt sich das Ganze jetzt in die technische Infrastruktur ein? Wie sieht es mit der Laufzeit aus? Woher bekommen wir Testdaten?
Build → Test → Deployment → Akzeptanz-Tests → Documentation
Gegen Entwicklungsumgebung
Gegen Ad-Hoc Server
Wir verwenden Jenkins als CI-Umgebung. Dieser führt mehrmals am Tag einen vollständigen Build durch und deployt die Applikation automatisch auf ein Testsystem. Sobald der Applikationsserver gestartet worden ist, beginnt der Akzeptanz-Test. Bei Fehlern werden sowohl der Product-Owner als auch der zuständige technische Architekt per Email verständigt.
Anschließend wird noch die aktuelle Systemspezifikation generiert.
Dieses Vorgehen ist durch das Buch [Continous Delivery] inspiriert.
Natürlich können Entwickler Tests auch lokal durchführen. Dabei spielt die Robustheit der Tests wieder eine Rolle. Die Tests können selbst die Applikation starten, inkl. InMemory-Datenbank, und die Tests dagegen ausführen.
Parallelisierung
Mehrere Testumgebungen
@VIP Tag
Sobald man mehrere Akzeptanz-Tests hat kann die Laufzeit der Tests zu einem Problem werden. Jenkins kann Jobs parallelisieren und die Ergebnisse wieder vereinen. Die Akzeptanz-Tests werden in mehrere Gruppen geteilt und parallel ausgeführt.
Zur Zeit haben wir noch in keinem Projekt große Probleme mit der Laufzeit. Die Parallelisierung haben wir daher erst bei einem Projekt umgesetzt. Da die Tests gegen Oracle laufen sollen, können wir keine echte Ad-hoc Umgebung verwenden, wir brauchen daher vier Testumgebungen, gegen die die Tests laufen (sonst könnte es vorkommen, dass sich die Test beeinflussen).
Sollte es wider Erwarten noch mehr Probleme mit der Laufzeit geben, haben wir für Szenarien @VIP Tags vorbereitet. Im Buildzyklus kann dann definiert werden, dass nur Szenarien ausgeführt werden die mit @VIP gekennzeichnet sind.
Test bereiten Daten selbst vor
Vorbereiten der Datenbank
DDL durch Liquibase
Vorbereiteter Mandant
Migrationsmechanismus
Grundsätzlich ist es so, dass alle Tests ihre Daten selbst vorbereiten. Dies geschieht mit Hilfe der "Given" Anweisungen. Hier ist wieder die Robustheit der Tests ein Thema.
Trotzdem ist es aber nötig, dass die Applikation beim Starten die Datenbank vorbereitet. Die Initialisierung bzw. die Aktualisierung erfolgt dabei durch Liquibase. Dann ist es noch notwendig eine Grundkonfiguration zu erstellen, also z.B. einen Mandanten für die Tests zu erzeugen.
Schließlich werden noch über einen Migrationsmechanismus, der in der Applikation sowieso existiert, Testdaten eingespielt. Diese Testdaten sind jedoch für die BDD-Tests nicht nötig. Sie dienen lediglich dazu die Umgebungen der Entwickler mit sinnvollen Daten zu befüllen.
Welche Frameworks und Tools werden bei der Prosche Informatik verwendet?
Verwaltet Feature-Files
Test-Framework Anbindung
Java, Ruby, .Net, Flex, ...
Cucumber ist das Framework, welches die Feature-Files verwaltet, liest und als Test ablaufen lässt. Cucumber steht dabei in mehreren Varianten zur Verfügung. Die Basisversion wurde für Ruby erstellt. Sehr bald gesellte sich aber Cucumber-JVM dazu, das rein in Java entwickelt wurde und die Hürden der Verwendung das Ruby-Frameworks beseitigte.
Cucumber liest die Feature-Files und erstellt daraus JUnit bzw. TestNG Testfälle. Dadurch ist die Integration zu allen wichtigen Tools wie IDEs und Buildtools vorhanden. In Eclipse oder IntelliJ z.B. lassen sich Feature-Files einfach als Unit-Tests starten.
Weiters stellt Cucumber noch eine Menge weiterer Module und Plugins zur Verfügung: Spring-Integration, HTML-Reports, Android Unterstützung, OSGI Unterstützung, u.v.m.
JBehave: Ähnlich zu Cucumber-JVM
JDave: Java-Files als Feature-File
easyb: Groovy-basiert
Gherkin: Sprachdefinition/-parser für Feature-Files
JBehave ist sehr ähnlich zu Cucumber-JVM. Es ist das ursprüngliche Framework von Dan North. Die aktivere Community scheint aber Cucumber zu haben.
JDave ist neuer und bietet die Möglichkeit die Feature-Files als Java-Klasse zu definieren. Für uns verliert sich hier jedoch die Lesbarkeit der Story-Files.
easyb basiert auf Groovy. Feature-Files können direkt in Groovy definiert werden.
Gherkin ist kein eigentliches BDD Framework, aber es bietet den Syntax-Parser für Feature-Files und ist daher hier aufgeführt.
Cucumber bietet keine Anbindung an einen Web- oder Rich-Client. Die oft referenzierten automatisierten GUI-Tests sind kein Feature der meisten BDD-Frameworks.
Für webbasierte Applikation kann Selenium als Steuerung des Clients verwendet werden. Es startet eine neue Instanz eines Browsers und Instrumentiert die Applikation mit einer Fernsteuerung. Weiters bietet es eine API, auf diese Fernsteuerung, von Javacode aus zugreifen zu können. Damit bietet es die Grundvoraussetzung GUI-Tests zu implementieren.
Automatisiert DDL Änderungen
Versionsverwaltung
XML-basiert und DB-unabhängig
Liquibase führt DDL-Changes anhand von XML-basierten Beschreibungsdateien (sog. ChangeSets) durch. In der DB selbst wird vermerkt welche ChangeSets schon eingespielt wurden und welche noch nicht.
Der Vorteil von Liquibase ist, dass, egal welchen Stand die Datenbank hat, diese auf die aktuelle Version gebracht werden kann. Das macht Liquibase dermaßen gut, dass in vielen Applikation keine direkten DDL Änderungen auf der Datenbank nötig sind, weder während der Entwicklung noch beim Einspielen von Updates. Die Zieldatenbank ist dabei egal. Die ChangeSets funktionieren sowohl gegen lokale Inmemory-DBs (z.B. HSQLDB) als auch gegen die großen Konzern-DBs wie Oracle oder DB2.
https://github.com/porscheinformatik/cucumber-report-db
Verwaltet Testergebnisse
Historische Vergleiche
Highscore
Hausintern werden alle Läufe von Akzeptanz-Tests zentral getrackt. Dadurch ist es möglich die Tests historisch zu vergleichen. Dies ist hilfreich um z.B. Tests mit langer Laufzeit oder geringer Robustheit zu finden.
Das Tool wurde von der Porsche Informatik entwickelt und ist Open Source.
http://thred.github.io/presentation-bdd/
Mit BDD können wir schaffen, dass alle Beteiligten die gleiche Sicht auf ein Produkt haben und wir können schaffen, dass wir (redundanten) Dokumentationsaufwand reduzieren.
Am Beginn muss für BDD doch mit einigem Aufwand gerechnet werden. Das rentiert sich nur bei größeren Projekten. Ist das Thema aber bei allen Beteiligten angekommen beginnen sich die Vorteile zu zeigen.
"Introduction BDD" von Dan North: ein Einführung ins Behavior Driven Development
http://behaviourdriven.org/: Webpräsenz speziell zum Thema Behavior Driven Development
BDD in Action: Behavior-driven development for the whole software lifecycle by Smart, John Ferguson (2014) Paperback (ASIN: B00RKQDB4E)