Akzeptanztests mit Gherkin, Cucumber und Selenium: Abnahmekriterien, die der Product Owner lesen kann
https://bildung.social/@oerinformatik/
https://oer-informatik.de/flurmonitor-akzeptanztest
Akzeptanztest, Abnahmetest, Gherkin, Cucumber, Behaviour Driven Development, Step Definition, Page Object, Selenium, Abnahmeprotokoll, Sprint Review, Product Owner, Scrum
tl/dr; (ca. 40 min Lesezeit): Die GUI-Tests aus dem letzten Artikel prüfen den Flurmonitor im Browser, aber nur die Developers können sie lesen. Jetzt schreibt das Team die Abnahmekriterien zusammen mit dem Product Owner als Gherkin-Szenarien („Angenommen … Wenn … Dann …“), und Cucumber führt sie mit den vorhandenen Page Objects in Chrome aus. Du lernst, wie Du einen Abnahmetest von einem Systemtest abgrenzt, wie gute Szenarien aussehen, wie Step Definitions mit Cucumber Expressions, Spring und einer verstellbaren Uhr arbeiten und wie aus dem Cucumber-Bericht ein Abnahmeprotokoll für das Sprint Review wird. Der erste Lauf findet zwei Abweichungen von den Anforderungen [AI-Assisted]. (Zuletzt geändert am 11.11.2026)
Dieser Artikel ist Teil einer Artikelreihe über den Flurmonitor mit Thymeleaf: Projektübersicht / Scrum-Projektstart / Thymeleaf einbinden / Templates / Ansichtsmodell / Integrationstest der Web-Schicht / Konfiguration und Formatierung / Aktualisierung / Hinweise mit Formularen / Robustheit und Betrieb / GUI-Test mit Selenium / Akzeptanztest / Change Request Keycloak
Schon fit in diesem Thema? Nutze die Kann-Liste und die Leitfragen unter dem Artikel als Checkliste, ob Du die relevanten Kompetenzen bereits hast.
Dieser Artikel baut auf der Kann-Liste des Artikels GUI-Tests mit Selenium und Page Objects auf.
Als Akzeptanztest-Grundlagen kann ich nach Durchlesen des Artikels…
… den Abnahmetest (Akzeptanztest) vom Systemtest abgrenzen: Prüfgrundlage, Sichtweise, wer entscheidet.
… die Bestandteile einer Gherkin-Datei benennen und erklären:
Funktionalität,Grundlage,Szenario,SzenariogrundrissmitBeispiele, Schritte mitAngenommen,Wenn,Dann,Und, Tags und Datentabellen.… ein Abnahmekriterium in der Form „Angenommen – Wenn – Dann“ als deklaratives Gherkin-Szenario in Fachsprache aufschreiben.
… ein Abnahmeprotokoll lesen und erklären, welche Belege es für jedes Abnahmekriterium braucht.
Darüber hinaus kann ich als Akzeptanztest-Expertenwissen…
… Cucumber über die JUnit Platform in ein Spring-Boot-Projekt einbinden und Step Definitions mit Cucumber Expressions, eigenen Parametertypen und Datentabellen schreiben.
… Step Definitions schlank halten, indem sie die Page Objects der GUI-Tests benutzen, und den Zustand eines Szenarios mit
@ScenarioScopeund Hooks verwalten.… Akzeptanztests in einer eigenen Gradle-Aufgabe bzw. Failsafe-Gruppe und in einem eigenen Pipeline-Job ausführen und den Cucumber-Bericht als Beleg ablegen.
… das Sprint Review als Abnahme durch den Product Owner gestalten und aus dem Ergebnis Folgen für das Product Backlog ableiten.
Thomas Becker ist Product Owner des Flurmonitors im Mitfahrlabor. Seit Sprint 0 stehen im Product Backlog Abnahmekriterien wie „Angenommen, ein Hinweis ist bis gestern gültig. Wenn ein Monitor die Seite lädt, dann erscheint der Hinweis nicht mehr.“ Seit dem letzten Artikel prüfen GUI-Tests in Chrome, was die Monitore zeigen. Die Tests heißen aber neuerHinweisErscheintAmMonitor() und bestehen aus Java-Code. Ob sie wirklich das prüfen, was im Product Backlog steht, kann Thomas Becker nicht erkennen. Im Sprint Review muss er sich bisher darauf verlassen, was die Developers ihm sagen.
Der zweite Eintrag von Sprint 3 ändert das: MON-12 „Abnahmekriterien als Akzeptanztests“. Die Kriterien sollen so aufgeschrieben werden, dass der Product Owner sie lesen und mitschreiben kann, und trotzdem bei jedem Push automatisch laufen. Das Bild zeigt den Weg, den ein Abnahmekriterium dabei nimmt: vom Product Backlog über eine Feature-Datei in Gherkin, die Step Definitions und die Page Objects bis in den Browser, und als Ergebnis zurück über den Cucumber-Bericht und das Abnahmeprotokoll ins Sprint Review.
Stand im Projekt: Sprint 3, Tag 4
Sprint 3 hat das Sprint Goal „Was die Monitore zeigen, prüfen am Ende des Sprints automatische Tests über den Browser – auch die Abnahmekriterien.“ MON-11 (5 Story Points) ist seit Tag 3 fertig und abgenommen. Offen sind MON-12 mit 8 Story Points und als Option MON-15 „Hinweise mit Beginn“. Auch MON-12 ist ein technischer Eintrag. Damit Thomas Becker ihn im Review abnehmen kann, hat das Team im Sprint Planning drei Abnahmekriterien vereinbart:
- AK-MON-12.1: Die Szenarien stehen in Gherkin auf Deutsch in Feature-Dateien; der Product Owner kann sie ohne Java-Kenntnisse lesen, und jeder Szenarioname beginnt mit der Nummer des Abnahmekriteriums.
- AK-MON-12.2: Die Szenarien laufen in der Pipeline in einem eigenen Job; der Bericht von Cucumber liegt als Artefakt vor, scheitert ein Szenario, ist die Pipeline rot.
- AK-MON-12.3: Jedes Abnahmekriterium von MON-01 bis MON-10 ist entweder als Szenario automatisiert oder im Abnahmeprotokoll mit einem anderen Beleg (Test, Sichtprüfung) aufgeführt.
Die Developers zerlegen MON-12 in Aufgaben und setzen die Nummern aus Sprint 3 fort:
| Nr. | Aufgabe im Sprint Backlog | Abschnitt |
|---|---|---|
| T56 | Abnahmekriterien mit dem Product Owner als Gherkin-Szenarien formulieren (Workshop) | Gherkin |
| T57 | Cucumber einbinden: Suite für die JUnit Platform, Spring-Kontext mit verstellbarer Uhr | Cucumber |
| T58 | Step Definitions mit den Page Objects aus MON-11, Parametertypen, Hooks für Testdaten | Step Definitions |
| T59 | Gradle-Aufgabe akzeptanzTest, Failsafe-Gruppe akzeptanz, Pipeline-Job akzeptanztest |
Pipeline |
| T60 | Funde aus dem ersten Lauf beheben | Erster Lauf |
| T61 | Abnahmeprotokoll für das Sprint Review vorbereiten | Abnahmeprotokoll |
Abnahmetest und Systemtest: zwei verschiedene Fragen
Im V-Modell stehen rechts oben zwei Teststufen, die beide die ganze Anwendung prüfen. Der Systemtest fragt: Hält das System, was der Entwurf festlegt? Grundlage sind Systementwurf und Schnittstellen, beim Flurmonitor die Seiten, ihre Adressen und das Verhalten im Browser. Die GUI-Tests aus MON-11 sind Systemtests. Der Abnahmetest, auch Akzeptanztest genannt, fragt: Erfüllt das System die vereinbarten Anforderungen? Grundlage sind die User Stories mit ihren Abnahmekriterien, also das, was der Product Owner im Namen der Nutzer*innen bestellt hat.
| Systemtest (GUI-Test aus MON-11) | Abnahmetest (MON-12) | |
|---|---|---|
| Prüfgrundlage | Systementwurf: Seiten, Adressen, Verhalten der Oberfläche | Anforderungen: User Stories und Abnahmekriterien |
| Sichtweise | Entwickler*innen: Funktioniert das System technisch? | Auftraggeber*innen: Ist es das, was bestellt wurde? |
| Sprache der Tests | Java mit Methodennamen wie flurBZeigtNurZurSchule() |
Gherkin mit den Sätzen des Product Backlogs |
| Wer schreibt | Developers | Product Owner und Developers gemeinsam |
| Wer entscheidet | Testergebnis: grün oder rot | Product Owner nimmt ab, die Tests liefern die Belege |
Die Technik ist dieselbe: Die ganze Anwendung läuft auf einem freien Port, Selenium steuert Chrome. Der Unterschied liegt darin, wogegen geprüft wird und wer das Ergebnis bewertet. Die Abnahme selbst ist immer eine Entscheidung von Menschen. Ein automatisierter Abnahmetest liefert die Belege dafür und läuft danach bei jeder Änderung erneut. Damit wird er zum Regressionstest für die Anforderungen.
Die Backend-Reihe hat Abnahmekriterien schon einmal automatisiert, im Artikel Systemtest und Abnahmetest: Dort wurde aus „Angenommen – Wenn – Dann“ ein JUnit-Test mit „Arrange – Act – Assert“, und die Nummer des Kriteriums stand im @DisplayName. Das funktioniert, aber die Übersetzung in Java bleibt eine Übersetzung. Ob angebotFuerGesternWirdAbgelehnt() wirklich AK-02.4 prüft, muss man im Code nachlesen.
Hier setzt Behaviour Driven Development (BDD) an: Fachseite und Entwicklung beschreiben das gewünschte Verhalten gemeinsam in einer strukturierten, aber natürlichen Sprache, und genau dieser Text wird ausgeführt. Ein verwandter Begriff ist Acceptance Test Driven Development (ATDD): Die Abnahmetests stehen fest, bevor der Code entsteht. Beim Flurmonitor ist der Code der meisten Stories schon fertig; das Team nutzt die Werkzeuge von BDD, um die vorhandenen Kriterien lesbar und ausführbar zu machen. Für die nächsten Stories, etwa MON-15, entstehen die Szenarien künftig schon im Refinement.
Gherkin: Abnahmekriterien als Szenarien
Gherkin ist die Sprache, in der Cucumber Szenarien erwartet. Eine Gherkin-Datei endet auf .feature und beschreibt eine Funktionalität (englisch Feature) mit mehreren Szenarien. Jede Zeile beginnt mit einem Schlüsselwort; alles danach ist freier Text. Gherkin gibt es in über 70 Sprachen. Die Kommentarzeile # language: de in der ersten Zeile schaltet auf die deutschen Schlüsselwörter um:
| Deutsch | Englisch | Bedeutung |
|---|---|---|
Funktionalität: |
Feature: |
Überschrift der Datei mit freier Beschreibung, meist die User Story |
Grundlage: |
Background: |
Schritte, die vor jedem Szenario der Datei laufen |
Szenario: |
Scenario: |
ein Beispiel, beim Flurmonitor genau ein Abnahmekriterium |
Szenariogrundriss: |
Scenario Outline: |
Vorlage mit Platzhaltern <name>, läuft einmal je Zeile der Beispiele |
Beispiele: |
Examples: |
Tabelle mit den Werten für die Platzhalter |
Angenommen |
Given |
Ausgangslage: Was ist vorher der Fall? |
Wenn |
When |
Ereignis: Was tut jemand? |
Dann |
Then |
erwartetes Ergebnis: Was ist danach zu sehen? |
Und, Aber |
And, But |
weiterer Schritt derselben Art |
@MON-01 |
@MON-01 |
Tag zum Gruppieren und Filtern |
\| … \| |
\| … \| |
Datentabelle, die zum Schritt davor gehört |
So sieht die Datei für die Hinweise der Schulverwaltung aus (Ausschnitt):
# language: de
@akzeptanz
Funktionalität: Hinweise der Schulverwaltung
Als Schulverwaltung möchte ich Hinweise mit Gültigkeitsdatum anlegen, die oberhalb der Fahrten erscheinen,
damit kurzfristige Änderungen wie ein Busausfall auf den Monitoren stehen (MON-07, MON-08).
Grundlage:
Angenommen es ist Dienstag, der 20.10.2026, 06:00 Uhr
@MON-07
Szenario: AK-MON-07.1 Ein neuer Hinweis erscheint auf allen Monitoren
Angenommen Heike Brinkmann ist an der Verwaltungsseite angemeldet
Wenn sie den Hinweis "Linie 61 fällt morgen aus" mit Gültigkeit bis 21.10.2026 speichert
Dann ist der Hinweis "Linie 61 fällt morgen aus" auf allen Monitoren zu sehen
@MON-07
Szenario: AK-MON-07.2 Ein abgelaufener Hinweis erscheint nicht mehr
Angenommen es gibt den Hinweis "Der Kirchplatz in Werther ist gesperrt" mit Gültigkeit bis 19.10.2026
Wenn der Monitor "foyer" die Seite lädt
Dann ist der Hinweis "Der Kirchplatz in Werther ist gesperrt" nicht zu sehen
@MON-08
Szenariogrundriss: AK-MON-08.2 Ein Hinweis für einen Haltepunkt erscheint nur dort
Angenommen es gibt den Hinweis "Treffpunkt heute am Bahnhofsvorplatz" nur für den Haltepunkt "Herford" mit Gültigkeit bis 21.10.2026
Wenn der Monitor für den Haltepunkt "<haltepunkt>" die Seite lädt
Dann ist der Hinweis "Treffpunkt heute am Bahnhofsvorplatz" <sichtbar>
Beispiele:
| haltepunkt | sichtbar |
| Herford | zu sehen |
| Gütersloh | nicht zu sehen |
Die Grundlage hält die Uhr fest: Wie in allen System- und GUI-Tests ist „heute“ Dienstag, der 20.10.2026, und „morgen“ der 21.10.2026. Der Szenariogrundriss für AK-MON-08.2 läuft zweimal, einmal je Zeile der Beispiele. Cucumber ersetzt dabei die Platzhalter im Text, bevor es nach einer passenden Step Definition sucht. Aus <sichtbar> wird also einmal „zu sehen“ und einmal „nicht zu sehen“. Tags wie @MON-07 gelten für das Szenario darunter, @akzeptanz über der Funktionalität für alle Szenarien der Datei.
Gute Szenarien schreiben
Ein Szenario beschreibt, was jemand erreicht, nicht wie er sich durch die Oberfläche klickt. Vergleiche zwei Fassungen von AK-MON-07.4:
# imperativ: beschreibt die Bedienung – bricht bei jeder Änderung der Seite
Szenario: Hinweis ohne Text
Angenommen ich öffne "/login"
Und ich tippe "Sekretariat-HB" in das Feld "username"
Und ich tippe "mitfahren" in das Feld "password"
Und ich klicke auf "Anmelden"
Und ich klicke auf "Speichern"
Dann sehe ich ein Element mit der Klasse "invalid-feedback"
# deklarativ: beschreibt das Verhalten in Fachsprache
Szenario: AK-MON-07.4 Ein Hinweis ohne Text wird nicht gespeichert
Angenommen Heike Brinkmann ist an der Verwaltungsseite angemeldet
Wenn sie einen Hinweis ohne Text mit Gültigkeit bis 21.10.2026 speichert
Dann zeigt das Formular die Meldung "Bitte einen Hinweistext eingeben." und behält das Datum 21.10.2026
Und wurde kein Hinweis gespeichert
Die erste Fassung kann Thomas Becker zwar lesen, aber sie sagt ihm nichts über die Anforderung, und sie enthält Adressen, Feldnamen und CSS-Klassen. Die zweite Fassung benutzt die Begriffe aus dem Product Backlog; wie man sich anmeldet, steht im Code dahinter. Für das Team gelten deshalb diese Regeln:
- Ein Szenario, ein Verhalten: genau ein
Wenn. Braucht ein Szenario zwei Ereignisse, sind es zwei Szenarien oder ein Szenariogrundriss. - Fachsprache statt Oberfläche: keine Adressen, IDs, CSS-Klassen oder Klickfolgen; Personen und Begriffe aus dem Szenario („Heike Brinkmann“, „Monitor im Foyer“).
- Konkrete Beispiele: „bis 21.10.2026“ statt „ein gültiges Datum“. Konkrete Werte decken Missverständnisse auf.
- Unabhängig: Jedes Szenario legt seine Daten selbst an und hängt nicht von der Reihenfolge ab.
- Nachvollziehbar: Der Name beginnt mit der Nummer des Kriteriums (AK-MON-12.1), Tags nennen die Story.
Der Workshop mit dem Product Owner
An Tag 4 setzen sich Thomas Becker und die beiden Developers Oksana Melnyk und Jonas Wolff, der die Prüfungen bisher von Hand gemacht hat, eine Stunde zusammen. In der agilen Praxis heißt dieses Treffen von Fachseite, Entwicklung und Test oft Three Amigos oder Specification Workshop. Sie gehen die 27 Abnahmekriterien von MON-01 bis MON-10 durch und entscheiden für jedes, ob es ein Szenario wird.
Zwei Dinge fallen dabei auf. Erstens lassen sich nicht alle Kriterien sinnvoll im Browser prüfen: „aus 5 m Entfernung lesbar“ (AK-MON-02.1) bleibt eine Sichtprüfung, und dass die Schul-IT nur die Einstellungen ändert (AK-MON-05.3), führt das Team im Review vor. Andere Kriterien sind schon durch Tests auf einer tieferen Ebene belegt, etwa das Verhalten bei einer Störung durch den FlurmonitorStoerungTest. Ein Abnahmetest muss nicht alles im Browser wiederholen; das Abnahmeprotokoll nennt dann den anderen Beleg (AK-MON-12.3).
Zweitens zwingt das Aufschreiben mit konkreten Werten zur Genauigkeit. AK-MON-03.2 lautet: „…, dann zeigt er höchstens 5, die frühesten zuerst.“ Beim Ausfüllen der Beispieltabelle fragt Oksana Melnyk: höchstens 5 insgesamt oder je Richtung? Der Monitor im Foyer zeigt beide Richtungen in zwei Tabellen. Thomas Becker entscheidet „je Richtung“, und das Kriterium wird im Product Backlog präzisiert. Am Ende stehen 16 Szenarien in zwei Feature-Dateien unter src/test/resources/features/: offene-fahrten.feature (MON-01, MON-03, MON-04, MON-05) und hinweise.feature (MON-07, MON-08).
Cucumber einbinden
Cucumber ist das bekannteste Werkzeug, das Gherkin ausführt. Es liest die Feature-Dateien, sucht zu jedem Schritt eine passende Java-Methode, ruft sie auf und schreibt einen Bericht. Für Java gibt es Cucumber-JVM; die Module werden über eine Stückliste (BOM, Bill of Materials) in einer gemeinsamen Version eingebunden. Alternativen sind zum Beispiel JBehave (ebenfalls Gherkin-ähnlich), Serenity BDD (setzt auf Cucumber auf und erzeugt ausführlichere Berichte) oder Karate (eigene Gherkin-Variante, vor allem für API-Tests).
In build.gradle.kts unter dependencies:
// Sprint 3 (MON-12): Abnahmekriterien als Gherkin-Szenarien; Cucumber führt sie über die JUnit Platform aus,
// cucumber-spring startet dafür denselben Spring-Kontext wie die GUI-Tests
testImplementation(platform("io.cucumber:cucumber-bom:8.0.4"))
testImplementation("io.cucumber:cucumber-java")
testImplementation("io.cucumber:cucumber-spring")
testImplementation("io.cucumber:cucumber-junit-platform-engine")
testImplementation("org.junit.platform:junit-platform-suite")cucumber-java bringt die Annotationen für Step Definitions, cucumber-spring verbindet Cucumber mit dem Spring-Testkontext. Ausgeführt werden die Szenarien über die JUnit Platform, auf der auch alle bisherigen Tests laufen: cucumber-junit-platform-engine ist eine eigene Test-Engine neben JUnit Jupiter, junit-platform-suite liefert die Annotationen für eine Testsuite. Diese Suite ist der Einstieg, den Gradle und Maven finden:
// failIfNoTests: In den Aufgaben test, systemTest und guiTest filtert der Tag alle Szenarien heraus
@Suite(failIfNoTests = false)
@IncludeEngines("cucumber")
@SelectPackages("features") // Ordner src/test/resources/features im Klassenpfad
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "de.dbh.mitfahrlabor.akzeptanztest")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "pretty, html:build/reports/cucumber/akzeptanztest.html, "
+ "junit:build/reports/cucumber/akzeptanztest.xml")
class AkzeptanztestSuite {
}@SelectPackages("features") wählt alle Feature-Dateien im Ordner features des Klassenpfads. Glue („Klebstoff“) nennt Cucumber den Code, der die Szenarien mit der Anwendung verbindet: Step Definitions, Hooks und Parametertypen im Paket akzeptanztest. Die Plugins schreiben die Schritte farbig auf die Konsole (pretty), einen HTML-Bericht und eine XML-Datei im JUnit-Format, die GitLab als Testbericht anzeigt.
Step Definitions: vom Satz zur Methode
Eine Step Definition ist eine Java-Methode mit einer Annotation, deren Text zu einem Schritt passt. Die Annotationen gibt es in jeder Sprache von Gherkin; im Paket io.cucumber.java.de heißen sie @Angenommen, @Wenn, @Dann und @Und. Für Cucumber ist das Schlüsselwort beim Zuordnen gleichgültig: Ein Schritt Und es gibt … findet auch eine Methode mit @Angenommen("es gibt …"). Die Schlüsselwörter sind für die Lesenden da.
Der Text der Annotation ist eine Cucumber Expression. Feste Wörter müssen genau passen, Parameter in geschweiften Klammern werden umgewandelt und als Argumente übergeben:
| Ausdruck | passt zu | wird zu |
|---|---|---|
{string} |
"Linie 61 fällt morgen aus" |
String ohne Anführungszeichen |
{int} |
2 |
int bzw. Integer |
{word} |
Dienstag |
String (ein Wort ohne Leerzeichen) |
Platz/Plätzen |
Platz oder Plätzen |
– (Alternative) |
offene(s) |
offene oder offenes |
– (optionaler Text) |
{datum} |
21.10.2026 |
LocalDate (eigener Parametertyp) |
Eigene Parametertypen machen die Step Definitions kürzer und die Szenarien natürlicher. Ein Parametertyp besteht aus einem regulären Ausdruck und einer Methode, die den gefundenen Text umwandelt; ihr Name ist der Name in den geschweiften Klammern:
public class Parametertypen {
private static final DateTimeFormatter DATUM = DateTimeFormatter.ofPattern("dd.MM.yyyy");
@ParameterType("\\d{2}\\.\\d{2}\\.\\d{4}")
public LocalDate datum(String text) {
return LocalDate.parse(text, DATUM);
}
@ParameterType("\\d{2}:\\d{2}")
public LocalTime uhrzeit(String text) {
return LocalTime.parse(text);
}
@ParameterType("zur Schule|nach Hause")
public Fahrtrichtung richtung(String text) {
return text.equals("zur Schule") ? Fahrtrichtung.ZUR_SCHULE : Fahrtrichtung.NACH_HAUSE;
}
}Mit diesen Typen passt eine einzige Step Definition zu allen Sätzen der Form „es gibt ein offenes Angebot ab … am … um … Uhr mit … freien Plätzen“:
@Angenommen("es gibt ein offenes Angebot ab {string} {richtung} am {datum} um {uhrzeit} Uhr "
+ "mit {int} freien Platz/Plätzen")
public void offenesAngebot(String ort, Fahrtrichtung richtung, LocalDate datum, LocalTime zeit, int plaetze) {
anlegen(BEN, ort, richtung, datum, zeit, plaetze);
}Findet Cucumber zu einem Schritt keine Methode, ist er undefined. Das Szenario scheitert, und Cucumber schlägt passende Methoden vor. Für das Szenario, das Heike Brinkmann im Review vorschlägt (siehe unten), sah das so aus:
io.cucumber.junit.platform.engine.UndefinedStepException: The step 'es gibt einen Hinweis mit 140 Zeichen' and 1 other step(s) are undefined.
You can implement these steps using the snippet(s) below:
@Angenommen("es gibt einen Hinweis mit {int} Zeichen")
public void es_gibt_einen_hinweis_mit_zeichen(Integer int1) {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
@Dann("steht der Hinweis vollständig auf dem Monitor")
public void steht_der_hinweis_vollständig_auf_dem_monitor() {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
Passen dagegen zwei Methoden auf denselben Schritt, meldet Cucumber eine AmbiguousStepDefinitionsException. Deshalb lohnt es sich, die Sätze im Team zu vereinheitlichen: Jeder Satz hat genau eine Bedeutung und genau eine Methode.
Datentabellen
Hängt an einem Schritt eine Tabelle, bekommt die Methode sie als letztes Argument. Cucumber wandelt sie je nach Typ des Parameters um: eine Spalte in List<String>, zwei Spalten in Map<String, String>, eine Tabelle mit Kopfzeile in List<Map<String, String>>. AK-MON-01.1 nutzt die zweispaltige Form, damit die erwartete Zeile wie ein Formular aussieht:
Szenario: AK-MON-01.1 Ein offenes Angebot erscheint mit allen Angaben
Angenommen es gibt ein offenes Angebot ab "Gütersloh" zur Schule am 21.10.2026 um 07:00 Uhr mit 2 freien Plätzen
Wenn der Monitor "foyer" die Seite lädt
Dann zeigt er bei den Fahrten zur Schule diese Fahrt:
| Tag | Mi 21.10. |
| Abfahrt | 07:00 |
| Ort | Gütersloh |
| Treffpunkt | Hauptbahnhof, ZOB Steig 3 |
| Freie Plätze | 2 |
@Dann("zeigt er bei den Fahrten {richtung} diese Fahrt:")
public void zeigtFahrt(Fahrtrichtung richtung, Map<String, String> erwartet) {
assertThat(welt.monitor().abfahrten(bereich(richtung))).anySatisfy(zeile -> {
assertThat(zeile.tag()).isEqualTo(erwartet.get("Tag"));
assertThat(zeile.abfahrt()).isEqualTo(erwartet.get("Abfahrt"));
assertThat(zeile.ort()).isEqualTo(erwartet.get("Ort"));
assertThat(zeile.treffpunkt()).isEqualTo(erwartet.get("Treffpunkt"));
assertThat(zeile.plaetze()).isEqualTo(erwartet.get("Freie Plätze"));
});
}Nebenbei belegt dieses Szenario AK-MON-02.3: Das Datum steht als „Mi 21.10.“, die Uhrzeit als „07:00“.
Spring und Cucumber: ein Kontext, eine Welt je Szenario
Die Szenarien brauchen dieselbe Umgebung wie die GUI-Tests: die ganze Anwendung auf einem freien Port und eine feste Uhr. Genau eine Klasse im Glue-Paket trägt dafür @CucumberContextConfiguration; ihre Annotationen bestimmen den Spring-Kontext, den Cucumber für alle Szenarien einmal startet:
@CucumberContextConfiguration
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT,
properties = "mitfahrlabor.flurmonitor.aktualisierung=2s")
@Import(Testwelt.class)
class CucumberSpringKonfiguration {
/** Startwert wie in allen System- und GUI-Tests: Dienstag, 20.10.2026, 06:00 Uhr. */
static final LocalDateTime START = LocalDateTime.of(2026, 10, 20, 6, 0);
@TestBean
private Clock uhr;
static Clock uhr() {
return new VerstellbareUhr(START);
}
}Neu gegenüber der GuiTestBasis ist die Uhr. AK-MON-03.1 beginnt mit „Angenommen, heute ist Freitag“; eine fest stehende Clock.fixed(…) lässt sich aber nicht verstellen. Die Klasse VerstellbareUhr erbt von java.time.Clock und hat zusätzlich eine Methode stellen(LocalDateTime). @TestBean ersetzt die Bean uhr der Anwendung durch sie, und eine Step Definition stellt sie:
@Angenommen("es ist {word}, der {datum}, {uhrzeit} Uhr")
public void esIst(String wochentag, LocalDate datum, LocalTime zeit) {
uhr.stellen(LocalDateTime.of(datum, zeit));
}Für jedes Szenario legt Cucumber neue Objekte aller Step-Definition-Klassen an. Was mehrere Klassen gemeinsam brauchen, steht in der Testwelt: der Browser, die Adresse des Testservers und der zuletzt geladene Monitor. Die Annotation @ScenarioScope macht sie zu einer Bean, die für jedes Szenario neu entsteht; Spring gibt dasselbe Objekt an alle Step Definitions dieses Szenarios weiter. Sie ist mit @Import nur im Kontext der Akzeptanztests registriert, nicht in den übrigen Tests der Anwendung:
@ScenarioScope
public class Testwelt {
@LocalServerPort
private int port;
private WebDriver browser;
private FlurmonitorSeite monitor;
/** Startet Chrome beim ersten Zugriff; Szenarien ohne Browser kommen so ohne Chrome aus. */
public WebDriver browser() {
if (browser == null) {
ChromeOptions optionen = new ChromeOptions();
if (!Boolean.getBoolean("gui.sichtbar")) {
optionen.addArguments("--headless=new");
}
optionen.addArguments("--window-size=1920,1080", "--lang=de-DE");
if (System.getenv("CI") != null) {
optionen.addArguments("--no-sandbox", "--disable-dev-shm-usage");
}
browser = new ChromeDriver(optionen);
browser.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(30));
}
return browser;
}
public String basis() {
return "http://localhost:" + port;
}
// monitor(), monitor(FlurmonitorSeite), browserBeenden() …
}Hooks: vor und nach jedem Szenario
Weil die Anwendung für alle Szenarien weiterläuft, bleiben Angebote und Hinweise eines Szenarios in der Datenbank, und der Cache des Monitors merkt sich alte Ergebnisse. Hooks sind Methoden, die Cucumber vor (@Before) und nach (@After) jedem Szenario aufruft. Die Klasse Vorbereitung stellt vorher die Uhr zurück, leert die Caches und merkt sich die IDs der vorhandenen Daten. Nachher löscht sie alles, was das Szenario angelegt hat, und beendet den Browser. Ist das Szenario gescheitert, hängt sie vorher ein Bildschirmfoto an den Bericht an:
@After
public void nachDemSzenario(Scenario szenario) {
if (szenario.isFailed() && welt.hatBrowser()) {
// Bildschirmfoto in den Cucumber-Bericht: zeigt, was der Browser im Moment des Fehlers sah
byte[] bild = ((TakesScreenshot) welt.browser()).getScreenshotAs(OutputType.BYTES);
szenario.attach(bild, "image/png", "Bildschirmfoto");
}
welt.browserBeenden();
angebote.deleteAllById(angebote.findAll().stream().map(Angebot::getId)
.filter(id -> !angeboteVorher.contains(id)).toList());
hinweise.deleteAllById(hinweise.findAll().stream().map(Hinweis::getId)
.filter(id -> !hinweiseVorher.contains(id)).toList());
caches.getCacheNames().forEach(name -> caches.getCache(name).clear());
}Ein @Transactional mit Rollback wie in den Repository-Tests hilft hier nicht: Die Anwendung bearbeitet die Anfragen des Browsers in eigenen Threads und eigenen Transaktionen, die der Test nicht zurückrollen kann.
Das Sequenzdiagramm zeigt, in welcher Reihenfolge Cucumber die Hooks und Step Definitions für das Szenario AK-MON-07.2 aufruft und wie die Step-Definition-Klassen über die gemeinsame Testwelt den geladenen Monitor weitergeben:
Page Objects wiederverwenden
Die Step Definitions sollen so schlank sein wie die Szenarien selbst. Die Arbeit im Browser erledigen die Page Objects aus MON-11; sie bekommen nur wenige neue Methoden, etwa meldungOhneFahrten(bereich) für den Satz aus AK-MON-01.4, quelltext() für die Datenschutzprüfung und oeffnenFuerHaltepunkt(…). Die Daten für ein „Angenommen“ legen die Step Definitions direkt über die Service-Schicht an, nicht über die Oberfläche. Das ist schneller und testet nur das, worum es im Szenario geht. So sehen die Schritte zu den Hinweisen aus:
public class HinweisSchritte {
private final Testwelt welt;
private final HinweisService hinweise;
private final HaltepunktRepository haltepunkte;
private HinweisVerwaltungSeite verwaltung;
public HinweisSchritte(Testwelt welt, HinweisService hinweise, HaltepunktRepository haltepunkte) {
this.welt = welt;
this.hinweise = hinweise;
this.haltepunkte = haltepunkte;
}
@Angenommen("Heike Brinkmann ist an der Verwaltungsseite angemeldet")
public void heikeBrinkmannAngemeldet() {
verwaltung = AnmeldeSeite.oeffnen(welt.browser(), welt.basis()).anmelden("Sekretariat-HB", "mitfahren");
}
@Angenommen("es gibt den Hinweis {string} mit Gültigkeit bis {datum}")
public void hinweisFuerAlle(String text, LocalDate bis) {
hinweise.legeAn(text, null, bis, Wichtigkeit.INFO);
}
@Wenn("sie den Hinweis {string} mit Gültigkeit bis {datum} speichert")
public void hinweisSpeichern(String text, LocalDate bis) {
verwaltung = verwaltung.anlegen(text, bis, "INFO");
}
@Dann("ist der Hinweis {string} nicht zu sehen")
public void nichtZuSehen(String text) {
assertThat(welt.monitor().hinweise()).noneMatch(h -> h.contains(text));
}
// weitere Schritte …
}Die Abhängigkeiten kommen über den Konstruktor: cucumber-spring erzeugt die Step-Definition-Klassen als Beans und kann deshalb Services, Repositories und die Testwelt einsetzen. Pseudonym und Passwort von Heike Brinkmann stehen nur hier, nicht in der Feature-Datei.
Bei AK-MON-01.3 („weder der Name noch das Pseudonym im HTML“) zeigt sich der Unterschied zwischen Fachsprache und Prüfung. Das Szenario sagt, was nicht zu sehen sein darf, die Step Definition sucht im ganzen Quelltext der Seite, auch in Attributen und Kommentaren:
Szenario: AK-MON-01.3 Der Monitor verrät nicht, wer fährt
Angenommen "Ben Richter" mit dem Pseudonym "Funkenflug" bietet eine Fahrt ab "Gütersloh" zur Schule am 21.10.2026 um 07:20 Uhr an
Wenn der Monitor "foyer" die Seite lädt
Dann kommt keiner dieser Texte im HTML der Seite vor:
| Ben |
| Richter |
| Funkenflug |
Der erste Lauf: zwei rote Szenarien
An Tag 7 laufen alle 16 Szenarien zum ersten Mal vollständig. 14 sind grün, zwei rot. Der Bericht zeigt bei jedem roten Szenario den Schritt, an dem es scheiterte.
Fund 1: Die Validierung hat ihre eigene Uhr
Dann ist der Hinweis "Linie 61 fällt morgen aus" auf allen Monitoren zu sehen
java.lang.AssertionError: [Monitor foyer]
Expecting any elements of:
["Hinweis der Schulverwaltung
Warnung: Park & Ride Herford ist wegen Bauarbeiten gesperrt. Treffpunkt ist der Bahnhofsvorplatz.
gilt bis Mi 18.11.",
"Hinweis der Schulverwaltung
Am Freitag endet der Unterricht schon um 12:15 Uhr. Bitte sprecht eure Rückfahrten ab.
gilt bis Fr 20.11."]
to match given predicate but none did.
Der Hinweis „bis morgen“ wurde nicht gespeichert. Das Bildschirmfoto im Bericht zeigt das Formular mit der Meldung am Feld „gültig bis“: Das Datum liege in der Vergangenheit. Für die Anwendung ist „heute“ der 20.10.2026, für die Validierung nicht. Die Annotation @FutureOrPresent am Feld bis des HinweisFormular fragt die Uhr der Bean Validation, und die ist ohne weitere Einstellung die Systemuhr des Rechners. Der GUI-Test aus MON-11 hatte das nicht bemerkt, weil er ein Datum im Dezember 2026 verwendete, das auch nach der Systemuhr in der Zukunft lag.
Betroffen sind nicht nur Tests. Das Profil vorfuehrung aus Sprint 2 stellt die Uhr der Anwendung fest auf einen Tag im Oktober 2026; auch dort könnte Heike Brinkmann keinen Hinweis für „morgen“ anlegen. Spring Boot erlaubt, die Konfiguration der Bean Validation anzupassen. In der UhrKonfiguration bekommt sie dieselbe Uhr wie die Services:
@Bean
public ValidationConfigurationCustomizer uhrFuerValidierung(Clock uhr) {
return konfiguration -> konfiguration.clockProvider(() -> uhr);
}Fund 2: ein Kriterium, das niemand umgesetzt hat
Dann zeigt er bei den Fahrten zur Schule genau diese Abfahrten:
Expecting actual:
["Di 20.10. 06:50", "Mi 21.10. 06:40", "Mi 21.10. 06:55", "Mi 21.10. 07:05", "Mi 21.10. 07:15", "Mi 21.10. 07:30"]
to contain exactly (and in same order):
["Di 20.10. 06:50", "Mi 21.10. 06:40", "Mi 21.10. 06:55", "Mi 21.10. 07:05", "Mi 21.10. 07:15"]
but some elements were not expected:
["Mi 21.10. 07:30"]
Das Szenario zu AK-MON-03.2 legt fünf Angebote ab Bielefeld für Mittwoch an; mit dem Angebot aus den Startdaten für Dienstag sind es sechs. Der Monitor zeigt alle sechs. Die MonitorAufbereitung holt bis zu 50 Angebote, wie es der API-Vertrag als Obergrenze erlaubt, filtert nach Zeitfenster und Richtung und sortiert sie. Eine Begrenzung auf 5 hat in Sprint 1 niemand eingebaut, und kein Test hat danach gefragt: Die Komponententests prüfen Zeitfenster und Sortierung, die Integrationstests die Darstellung. Das ist dieselbe Art Lücke, die der Abnahmetest des Backends bei AK-02.4 gefunden hat. Die Korrektur ist eine Zeile:
/** Höchstens so viele Zeilen je Richtung, damit die Seite ohne Scrollen passt (AK-MON-03.2, Sprint 3). */
static final int ZEILEN_JE_RICHTUNG = 5;
// …
.sorted(FRUEHESTE_ZUERST)
.limit(ZEILEN_JE_RICHTUNG)
.toList();Nach beiden Korrekturen sind alle 16 Szenarien grün, ebenso alle übrigen Tests. Beide Funde gehören zu MON-12, weil sie zum Abschluss des Eintrags nötig sind; das Team trägt sie als T60 ins Sprint Backlog ein.
Akzeptanztests im Build und in der Pipeline
Die Szenarien brauchen wie die GUI-Tests einen installierten Chrome. Sie tragen deshalb den Tag akzeptanz (Cucumber gibt die Tags aus der Feature-Datei an die JUnit Platform weiter) und laufen in einer eigenen Aufgabe, nicht in ./gradlew check:
In build.gradle.kts schließt die Aufgabe test den Tag aus, eine neue Aufgabe nimmt nur ihn:
tasks.test {
useJUnitPlatform { excludeTags("system", "gui", "akzeptanz") }
}
// Akzeptanztests (Sprint 3, MON-12): Gherkin-Szenarien aus src/test/resources/features, Tag "akzeptanz";
// wie die GUI-Tests mit Chrome, deshalb nur auf Aufruf (./gradlew akzeptanzTest) und im eigenen Pipeline-Job
val akzeptanzTest by tasks.registering(Test::class) {
description = "Führt die Abnahmekriterien als Cucumber-Szenarien im Browser aus (Akzeptanztest)."
group = "verification"
testClassesDirs = sourceSets["test"].output.classesDirs
classpath = sourceSets["test"].runtimeClasspath
useJUnitPlatform { includeTags("akzeptanz") }
systemProperty("gui.sichtbar", System.getProperty("gui.sichtbar") ?: "false")
// nur bestimmte Szenarien, z. B. -Dcucumber.filter.tags="@MON-07"
System.getProperty("cucumber.filter.tags")?.let { systemProperty("cucumber.filter.tags", it) }
shouldRunAfter(tasks.test, systemTest, guiTest)
}Gestartet werden die Akzeptanztests so; mit cucumber.filter.tags laufen nur die Szenarien mit bestimmten Tags, hier die zu MON-07:
.\gradlew.bat akzeptanzTest
.\gradlew.bat akzeptanzTest "-Dcucumber.filter.tags=@MON-07" "-Dgui.sichtbar=true"
# mit Maven
.\mvnw.cmd test-compile failsafe:integration-test failsafe:verify "-Dfailsafe.gruppen=akzeptanz"In der Pipeline übernimmt der neue Job akzeptanztest mit extends die Stufe und die Installation von Chrome aus dem Job gui-test. Der Cucumber-Bericht bleibt ein Jahr als Artefakt erhalten; er ist der Beleg für das Abnahmeprotokoll (AK-MON-12.2):
# Akzeptanztest (Sprint 3, MON-12): Gherkin-Szenarien mit Cucumber; Chrome wie im Job gui-test (extends
# übernimmt before_script und stage). Der Cucumber-Bericht ist das Beweisstück für das Abnahmeprotokoll.
akzeptanztest:
extends: gui-test
script:
- ./gradlew akzeptanzTest
artifacts:
when: always
paths:
- build/reports/cucumber/
expire_in: 1 year
reports:
junit: build/reports/cucumber/akzeptanztest.xmlDas Abnahmeprotokoll
Wie im Backend hält ein Abnahmeprotokoll fest, was gegen welche Anforderungen geprüft wurde, mit welchem Ergebnis und welche Entscheidung daraus folgt. Neu ist die Spalte „Beleg“ je Kriterium (AK-MON-12.3). Das vollständige Protokoll liegt als abnahmeprotokoll-sprint-3.md im Code-Stand; hier der Kopf und ein Ausschnitt:
| Abnahmeprotokoll | |
|---|---|
| Gegenstand | Flurmonitor im Mitfahrlabor-Backend, Stand src/a11-akzeptanztest |
| Grundlage | Product Backlog MON-01 bis MON-10 mit 27 Abnahmekriterien, Definition of Done (Stand Retro 2) |
| Durchführung | ./gradlew akzeptanzTest (16 Szenarien, Chrome ohne Fenster, Uhr Di 20.10.2026 06:00); weitere Belege aus GUI-, Integrations- und Komponententests; Sichtprüfung im Foyer |
| Ergebnis 1. Lauf | 14 von 16 Szenarien grün; AK-MON-07.1 und AK-MON-03.2 nicht erfüllt |
| Maßnahmen | T60: Uhr der Anwendung auch für Bean Validation, höchstens 5 Zeilen je Richtung |
| Ergebnis 2. Lauf | 16 von 16 Szenarien grün; check und guiTest grün |
| Entscheidung | MON-11 und MON-12 abgenommen; Freigabe für alle vier Monitore |
| Datum, Unterschrift | Tag 10 von Sprint 3, Thomas Becker (Product Owner) |
| Nicht geprüft | mehr als 50 offene Angebote, andere Browser als Chrome, Lesbarkeit an den Monitoren außerhalb des Foyers |
| Kriterium | Beleg | Ergebnis |
|---|---|---|
| AK-MON-01.1 | Szenario | erfüllt |
| AK-MON-01.2 | MonitorAufbereitungTest, FlurmonitorControllerTest |
erfüllt |
| AK-MON-02.1 | Sichtprüfung aus 5 m im Foyer (Review) | erfüllt |
| AK-MON-03.2 | Szenario | 1. Lauf nicht erfüllt, 2. Lauf erfüllt |
| AK-MON-05.3 | Vorführung im Review: Richtung eines Monitors nur in den Einstellungen geändert | erfüllt |
| AK-MON-06.1 | AktualisierungGuiTest |
erfüllt |
| AK-MON-07.1 | Szenario | 1. Lauf nicht erfüllt, 2. Lauf erfüllt |
| AK-MON-10.1 | FlurmonitorStoerungTest |
erfüllt |
Ein Protokoll, in dem alle Kriterien „erfüllt“ heißen, ist nur so viel wert wie seine Belege. Die Zeile „Nicht geprüft“ sagt ehrlich, wo die Belege enden. Und die beiden Läufe zeigen, dass das Protokoll den Weg dokumentiert und nicht nur den Endstand.
Sprint Review 3: Abnahme durch den Product Owner
Im Sprint Review prüft das Scrum Team mit den Stakeholdern, was im Sprint entstanden ist, und passt das Product Backlog an. Ein Eintrag gilt als fertig, wenn er die Definition of Done erfüllt; ob er die Anforderungen erfüllt, entscheidet der Product Owner. Für den Flurmonitor folgt daraus für jeden Eintrag derselbe Ablauf:
An Tag 10 treffen sich im Foyer Thomas Becker, die betreuende Lehrkraft als Scrum Master, die Developers, Heike Brinkmann aus der Schulverwaltung und Svenja Krämer von der Schul-IT. Die Developers zeigen die Pipeline mit den Jobs gui-test und akzeptanztest, öffnen den Cucumber-Bericht und gehen die beiden roten Szenarien des ersten Laufs mit ihren Korrekturen durch. Am Monitor im Foyer prüft Thomas Becker selbst die Lesbarkeit aus fünf Metern (AK-MON-02.1); Svenja Krämer stellt den Monitor in der Mensa über die Einstellungen auf eine andere Richtung um (AK-MON-05.3).
Dann geht Thomas Becker das Abnahmeprotokoll Zeile für Zeile durch und nimmt MON-11 und MON-12 ab. Das Sprint Goal ist erreicht, ebenso das Produktziel: Alle vier Monitore zeigen Fahrten und Hinweise, aktualisieren sich selbst, enthalten keine personenbezogenen Daten und sind durch Tests über die Oberfläche abgesichert. Er gibt den Flurmonitor für alle vier Standorte frei.
Das Review hat außerdem Folgen für das Product Backlog:
- MON-15 „Hinweise mit Beginn“ war die Option und ist nicht begonnen. Sie wird nicht vorgeführt, bleibt oben im Product Backlog und zählt nicht zur Velocity. Sprint 3 bringt 13 Story Points; der Durchschnitt der drei Sprints liegt bei 14,7.
- AK-MON-07.5 (Vorschlag): Heike Brinkmann hat die Feature-Datei zu den Hinweisen gelesen und fragt, ob ein Hinweis mit den erlaubten 140 Zeichen vollständig auf dem Monitor steht. Daraus wird ein neues Kriterium für das nächste Refinement; den Text des Szenarios hat sie gleich selbst formuliert. Genau das soll AK-MON-12.1 möglich machen.
- AK-MON-03.2 ist mit „je Richtung“ präzisiert.
Die Retrospektive von Sprint 3 mit der offenen Frage, ob „Systemtest über den Browser grün“ in die Definition of Done kommt, folgt im nächsten Artikel.
Code-Stand
Den vollständigen Stand dieses Artikels findest Du im Ordner src/a11-akzeptanztest des Backend-Repositorys. Gegenüber dem Stand a10 sind neu bzw. geändert:
| Datei | Änderung |
|---|---|
build.gradle.kts, pom.xml |
Cucumber (BOM 8.0.4), JUnit Platform Suite; Aufgabe akzeptanzTest bzw. Failsafe-Gruppe akzeptanz |
.gitlab-ci.yml, ci/maven.gitlab-ci.yml |
neuer Job akzeptanztest |
UhrKonfiguration.java |
Uhr der Anwendung auch für Bean Validation (Fund 1) |
web/monitor/MonitorAufbereitung.java |
höchstens 5 Zeilen je Richtung (Fund 2) |
test/resources/features/*.feature |
neu: offene-fahrten.feature, hinweise.feature mit 16 Szenarien |
test/…/akzeptanztest/*.java |
neu: AkzeptanztestSuite, CucumberSpringKonfiguration, VerstellbareUhr, Testwelt, Vorbereitung, Parametertypen, MonitorSchritte, HinweisSchritte |
test/…/guitest/seiten/*.java |
FlurmonitorSeite und AbfahrtZeile um Methoden für die Szenarien ergänzt |
product-backlog.md, sprintplan.md |
AK-MON-03.2 präzisiert, Abnahmekriterien MON-12, T56–T61, Stand Ende Sprint 3 |
abnahmeprotokoll-sprint-3.md, review-sprint-3.md |
neu |
Der Startcode für die Übungen mit den Mitfahrgesuchen liegt wie bisher im Unterordner uebung-mitfahrgesuche.
Nächster Schritt: Change Request zur Anmeldung
Der Flurmonitor ist abgenommen. Die Verwaltungsseite meldet sich aber noch über das Formular von Spring Security an, die REST-API über HTTP Basic. Die Schul-IT möchte beides über den zentralen Anmeldedienst Keycloak laufen lassen. Im Artikel Change Request Keycloak baut das Team diesen Change Request ein, hält die Retrospektive von Sprint 3 und schaut in einer Projekt-Retrospektive auf alle drei Sprints zurück. Die Akzeptanztests aus diesem Artikel laufen dabei als Regressionstests mit.
In den Übungsaufgaben zu diesem Artikel bestimmst Du die Bestandteile einer Feature-Datei, schreibst Szenarien und Step Definitions für die Mitfahrgesuche und wertest ein Abnahmeprotokoll aus.
Leitfragen
- Was unterscheidet einen Abnahmetest von einem Systemtest, wenn beide die ganze Anwendung über den Browser prüfen?
Die Prüfgrundlage und die Sichtweise: Der Systemtest prüft gegen den Systementwurf (Seiten, Adressen, Verhalten), der Abnahmetest gegen die Anforderungen (User Stories mit Abnahmekriterien). Über die Abnahme entscheidet der Product Owner bzw. die Auftraggeber*innen; automatisierte Abnahmetests liefern die Belege. siehe Abschnitt „Abnahmetest und Systemtest“
- Aus welchen Bestandteilen besteht eine Feature-Datei in Gherkin?
Funktionalität mit Beschreibung, optional Grundlage für gemeinsame Schritte, mehrere Szenarios bzw. Szenariogrundrisse mit Beispielen; jedes Szenario besteht aus Schritten mit Angenommen, Wenn, Dann, Und, Aber. Dazu kommen Tags (@MON-07), Datentabellen und Kommentare wie # language: de. siehe Abschnitt „Gherkin“
- Woran erkennst Du ein gutes, deklaratives Szenario?
Es beschreibt ein Verhalten in Fachsprache mit genau einem Wenn, enthält konkrete Beispielwerte, aber keine Adressen, IDs, CSS-Klassen oder Klickfolgen, legt seine Daten selbst an und trägt die Nummer des Abnahmekriteriums. siehe Abschnitt „Gute Szenarien schreiben“
- Wie findet Cucumber zu einem Schritt die passende Java-Methode, und was passiert, wenn es keine oder zwei findet?
Cucumber vergleicht den Text des Schritts (ohne Schlüsselwort) mit den Cucumber Expressions der Annotationen im Glue-Paket; Parameter wie {string}, {int} oder eigene Typen werden umgewandelt und übergeben. Ohne Treffer ist der Schritt undefined, und Cucumber schlägt Methoden vor; bei zwei Treffern meldet es eine AmbiguousStepDefinitionsException. siehe Abschnitt „Step Definitions“
- Wozu dienen
@CucumberContextConfiguration,@ScenarioScopeund die Hooks@Beforeund@After?
Die Klasse mit @CucumberContextConfiguration legt den Spring-Kontext fest, den alle Szenarien teilen (hier ganze Anwendung, verstellbare Uhr). @ScenarioScope macht ein Objekt wie die Testwelt zu einer Bean, die für jedes Szenario neu entsteht und von allen Step Definitions geteilt wird. Die Hooks setzen vor jedem Szenario Uhr und Caches zurück und räumen danach Daten und Browser auf. siehe Abschnitt „Spring und Cucumber“
- Warum benutzen die Step Definitions Page Objects und legen Testdaten über die Service-Schicht an?
Die Page Objects kapseln Locatoren und Wartebedingungen, die Step Definitions bleiben so kurz wie die Sätze. Daten über die Service-Schicht anzulegen ist schneller und stabiler als über die Oberfläche und prüft im Szenario nur das, worum es geht. siehe Abschnitt „Page Objects wiederverwenden“
- Welche Lücken haben die Akzeptanztests gefunden, und warum hatten die anderen Tests sie übersehen?
@FutureOrPresent prüfte gegen die Systemuhr statt gegen die Uhr der Anwendung; der GUI-Test hatte ein Datum gewählt, das nach beiden Uhren in der Zukunft lag. Die Begrenzung auf 5 Fahrten je Richtung (AK-MON-03.2) hatte niemand umgesetzt; die Tests der unteren Ebenen prüften nur, was umgesetzt war. siehe Abschnitt „Der erste Lauf“
- Was gehört in ein Abnahmeprotokoll?
Gegenstand und Stand, Grundlage (Anforderungen, DoD), Durchführung, Ergebnisse aller Läufe, Maßnahmen, Entscheidung mit Datum und Unterschrift, je Kriterium Beleg und Ergebnis sowie, was nicht geprüft wurde. siehe Abschnitt „Das Abnahmeprotokoll“
- Wie läuft das Sprint Review als Abnahme ab, und was passiert mit einem Eintrag, der nicht fertig ist?
Nur Einträge, die die DoD erfüllen und deren Kriterien belegt sind, werden vorgeführt; der Product Owner nimmt sie ab, sie zählen zur Velocity. Ein Eintrag, der nicht fertig ist (hier die Option MON-15), wird nicht vorgeführt und geht zurück ins Product Backlog. Rückmeldungen der Stakeholder werden zu neuen oder geänderten Einträgen. siehe Abschnitt „Sprint Review 3“
Links und weitere Informationen
für den Einstieg: Ein Flurmonitor mit Thymeleaf: das Projekt im Überblick
als Grundlage: GUI-Tests mit Selenium und Page Objects, Systemtest und Abnahmetest der REST-API, User Stories und Scrum
zum Szenario: Szenario Mitfahrlabor mit Personas und Flurmonitoren
als Nachschlagewerk: Gherkin-Referenz und Gherkin-Schlüsselwörter in allen Sprachen
als Nachschlagewerk: Cucumber Expressions, Cucumber-JVM: JUnit Platform Engine und Cucumber-JVM: Spring
zum Weiterlesen: Scrum Guide 2020 (deutsch), Martin Fowler: GivenWhenThen und Cucumber: Better Gherkin
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Akzeptanztests mit Gherkin, Cucumber und Selenium: Abnahmekriterien, die der Product Owner lesen kann” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/flurmonitor-akzeptanztest veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/java-springboot/Backend. Erstellt mit KI-Assistenz; von mir umfassend redaktionell überarbeitet, geprüft und freigegeben. Stand: 11.11.2026.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]
