Cucumber BDD: Die Geheimwaffe hinter jedem erfolgreichen Projekt das ich liefere
Lass mich dir vom wichtigsten Tool in meinem Entwicklungs Arsenal erzählen. Es ist keine fancy IDE. Es ist kein KI Code Generator. Es ist Cucumber, und es war das Rückgrat jedes erfolgreichen Projekts das ich im letzten Jahrzehnt geliefert habe. Von Enterprise Kunden wie der British Army und Volvo Group bis zu SaaS Plattformen wie Stint.co und Regios.at, jedes einzelne läuft auf einem Fundament von Behaviour Driven Development. Das ist nicht optional. So wird professionelle Software gebaut.
Lass mich dir vom wichtigsten Tool in meinem Entwicklungs Arsenal erzählen. Es ist keine fancy IDE. Es ist kein KI Code Generator. Es ist Cucumber, und es war das Rückgrat jedes erfolgreichen Projekts das ich im letzten Jahrzehnt geliefert habe. Von Enterprise Kunden wie der British Army und Volvo Group bis zu SaaS Plattformen wie Stint.co und Regios.at, jedes einzelne läuft auf einem Fundament von Behaviour Driven Development. Das ist nicht optional. So wird professionelle Software gebaut.
Ich übertreibe nicht wenn ich sage dass Cucumber meinen Kunden Millionen an vermiedenen Bugs, Nacharbeit und Missverständnissen gespart hat. Wenn du diesen Post fertig gelesen hast, wirst du genau verstehen warum, und du wirst dich fragen wie du jemals Software ohne es gebaut hast.
Was ist Behaviour Driven Development?
Bevor ich speziell auf Cucumber eingehe, lass mich die Philosophie dahinter erklären.
Traditionelle Softwareentwicklung funktioniert ungefähr so:
- Business Person schreibt Anforderungsdokument
- Entwickler liest Dokument, interpretiert es auf seine Weise
- Entwickler schreibt Code basierend auf seiner Interpretation
- Tester testet basierend auf seiner Interpretation desselben Dokuments
- Business Person sieht das Ergebnis und sagt "das habe ich nicht gemeint"
- Alle streiten darüber wessen Interpretation richtig war
- Teure Nacharbeit passiert
- Wiederhole bis Budget aufgebraucht oder alle aufgeben
Das ist Wahnsinn. Wir bauen seit Jahrzehnten Software so und wundern uns warum Projekte scheitern.
Behaviour Driven Development (BDD) fixt das indem es alle dieselbe Sprache sprechen lässt. Statt vager Anforderungsdokumente die von jedem Leser anders interpretiert werden, nutzt BDD strukturierte, eindeutige Spezifikationen in klarer Sprache die auch als Tests ausführbar sind.
Die Schlüsselerkenntnis: wenn die Spezifikation der Test ist, kann sie nicht falsch interpretiert werden. Die Software verhält sich entweder wie spezifiziert oder nicht. Es gibt keinen Raum für "naja, ich dachte du meintest..." weil das Verhalten präzise definiert und automatisch verifiziert wird.
Cucumber kommt ins Spiel
Cucumber ist das Tool das BDD praktikabel macht. Es lässt dich Spezifikationen in einer Sprache namens Gherkin schreiben, die sich wie normales Deutsch (oder Englisch) liest aber genug Struktur hat um von einem Computer geparst und als Tests ausgeführt zu werden.
So sieht eine Cucumber Feature Datei aus:
# features/user_login.feature
# Das ist die echte Spezifikaiton UND der Test
# Wenn das durchläuft, funktioniert das Feature. So einfach.
Feature: User Login
As a registered user
I want to log into my account
So that I can access my personalised dashboard
Background:
Given a user exists with email "[email protected]" and password "correcthorse"
Scenario: Successful login with valid credentials
Given I am on the login page
When I fill in "Email" with "[email protected]"
And I fill in "Password" with "correcthorse"
And I press "Sign In"
Then I should see "Welcome back, Sarah!"
And I should be on the dashboard page
Scenario: Failed login with wrong password
Given I am on the login page
When I fill in "Email" with "[email protected]"
And I fill in "Password" with "wrongpassword"
And I press "Sign In"
Then I should see "Invalid email or password"
And I should still be on the login page
Lies das laut vor. Jeder kann es verstehen. Dein Product Manager kann es verstehen. Dein CEO kann es verstehen. Deine Oma könnte es wahrscheinlich verstehen. Das ist der Punkt.
Aber hier kommt die Magie: das ist nicht nur Dokumentation. Das ist ein ausführbarer Test. Wenn ich cucumber features/user_login.feature ausführe, öffnet Cucumber tatsächlich einen Browser, navigiert zur Login Seite, füllt die Felder aus, klickt den Button und verifiziert die Ergebnisse. Wenn sich die Anwendung nicht genau wie spezifiziert verhält, schlägt der Test fehl.
Warum ich Cucumber bei jedem einzelnen Projekt nutze
Lass mich konkret werden wo ich das eingesetzt habe:
British Army Missionskritische Systeme wo Bugs nicht nur unpraktisch sind, sondern gefährlich sein können. Jede Anforderung wird in Gherkin erfasst, von Stakeholdern geprüft und automatisch verifiziert.
Volvo Group Enterprise Logistiksysteme die Millionen an Transaktionen verarbeiten. Die Feature Dateien dienen als Vertrag zwischen Entwicklung und Business.
Stint.co SaaS Marketing Plattform wo Features wöchentlich shippen. Cucumber stellt sicher dass neue Features bestehende Funktionalität nicht kaputtmachen.
Regios.at Regionale Entdeckungsplattform mit komplexer Such und Filterlogik. Die Feature Dateien dokumentieren jeden Edge Case.
Icebreaker.com E-Commerce mit komplizierter Produktkonfiguration. Cucumber Tests verifizieren dass jede Kombination korrekt funktioniert.
Auto-Prammer.at Automobil Marktplatz mit ausgefeilten Matching Algorithmen. BDD Spezifikationen stellen sicher dass die Matching Logik sich genau so verhält wie das Business es beabsichtigt.
Das Muster ist konsistent: komplexe Anforderungen, hohe Einsätze, und null Toleranz für "ups, das habe ich nicht gemeint." Cucumber eliminiert diese ganze Problemkategorie.
Die Gherkin Sprache: Essentielle Konzepte
Gherkin hat ein kleines Vokabular das leicht zu lernen aber mächtig genug ist um jedes Verhalten zu beschreiben. Lass mich durch jedes Element gehen.
Feature
Jede .feature Datei beginnt mit einem Feature Block der beschreibt welche Fähigkeit spezifiziert wird:
Feature: Shopping Cart Management
As an online shopper
I want to manage items in my shopping cart
So that I can purchase multiple products in one transaction
Die dreizeilige Beschreibung folgt einem Standardformat:
- As a [Typ von User]
- I want [irgendeine Fähigkeit]
- So that [Business Wert]
Das ist nicht nur Fülltext. Es zwingt dich zu artikulieren warum das Feature existiert und für wen es ist. Wenn du diese drei Zeilen nicht ausfüllen kannst, verstehst du das Feature nicht gut genug um es zu bauen.
Scenario
Scenarios sind die einzelnen Testfälle. Jedes Scenario beschreibt ein spezifisches Verhalten:
Scenario: Adding a product to an empty cart
Given my shopping cart is empty
When I add "Blue Widget" to my cart
Then my cart should contain 1 item
And the cart total should be "€29.99"
Ein gutes Scenario ist:
- Unabhängig Es verlässt sich nicht darauf dass andere Scenarios zuerst laufen
- Fokussiert Es testet ein spezifisches Verhalten
- Klar Jeder kann verstehen was getestet wird
Given, When, Then
Das sind die Bausteine jedes Scenarios:
Given Richtet den initialen Kontext ein. In welchem Zustand ist das System vor der Aktion?
Given I am logged in as an admin user
Given there are 5 products in the database
Given the payment gateway is configured for test mode
When Beschreibt die durchgeführte Aktion. Was macht der User?
When I click "Delete Product"
When I submit the order form
When 24 hours have passed
Then Spezifiziert das erwartete Ergebnis. Was sollte als Resultat passieren?
Then I should see "Product deleted successfully"
Then an order confirmation email should be sent
Then the product should no longer appear in search results
And, But
Diese werden verwendet um mehrere Steps desselben Typs zu verketten:
Scenario: Complex order with multiple conditions
Given I am logged in as "[email protected]"
And I have a 20% discount code "SAVE20"
And my shipping address is set to Vienna
When I add "Fancy Gadget" to my cart
And I apply the discount code
And I proceed to checkout
Then I should see the discounted price of "€79.99"
And I should see free shipping applied
But I should not see the express delivery option
Das But Keyword ist nur syntaktischer Zucker für And mit negativen Konnotationen. Es liest sich natürlicher wenn man beschreibt was nicht passieren sollte.
Background
Wenn mehrere Scenarios dasselbe Setup teilen, nutze einen Background Block:
Feature: Admin Product Management
Background:
Given I am logged in as an admin
And I am on the product management page
Scenario: Creating a new product
When I click "Add New Product"
And I fill in the product details
And I click "Save"
Then I should see "Product created successfully"
Scenario: Editing an existing product
Given a product "Old Widget" exists
When I click "Edit" next to "Old Widget"
And I change the name to "New Widget"
And I click "Save"
Then I should see "Product updated successfully"
Der Background läuft vor jedem Scenario in der Datei. Das hält Scenarios fokussiert auf das was sie einzigartig macht statt Setup Steps zu wiederholen.
Step Definitions: Wo die Magie passiert
Feature Dateien beschreiben was passieren soll. Step Definitions sagen Cucumber wie es passieren soll. Sie sind der Klebstoff zwischen Spezifikationen in klarer Sprache und echtem Code.
Hier ist eine Step Definition Datei:
# features/step_definitions/authentication_steps.rb
# Diese Steps handlen das ganze Ein- und Ausloggen Zeug
# Wird quer durch basicly jede Feature Datei wiederverwendet
Given('I am logged in as {string}') do |email|
# User finden oder erstellen factory bot kümmert sich um details
@current_user = User.find_by(email: email) || create(:user, email: email)
# Tatsächlich über die UI einloggen
# Könnte schnellere Methode nutzen aber das testet den echten Flow
visit login_path
fill_in 'Email', with: email
fill_in 'Password', with: 'password123' # alle test user haben das
click_button 'Sign In'
# Sanity check sicherstellen das wir wirklich eingeloggt sind
expect(page).to have_content('Welcome')
end
Given('I am logged in as an admin') do
# Den anderen Step wiederverwenden aber mit admin user
# DRY Prinzipien gelten auch für Step Defs ne
@current_user = create(:user, :admin)
step %(I am logged in as "#{@current_user.email}")
end
Given('I am not logged in') do
# Sicherstellen das keine Session existiert
# Manchmal lassen Tests Crud zurück
Capybara.reset_sessions!
end
# features/step_definitions/form_steps.rb
# Generische Form Interaktions Steps
# Die werden in fast jedem Feature genutzt
When('I fill in {string} with {string}') do |field, value|
fill_in field, with: value
end
When('I select {string} from {string}') do |value, field|
select value, from: field
end
When('I press {string}') do |button|
click_button button
end
When('I click {string}') do |link_or_button|
# Erst Button versuchen, dann Link
# Deckt die meisten Fälle ab ohne separate Steps zu brauchen
begin
click_button link_or_button
rescue Capybara::ElementNotFound
click_link link_or_button
end
end
# features/step_definitions/assertion_steps.rb
# Sachen auf der Seite checken
# Halt diese generisch damit sie überall funktionieren
Then('I should see {string}') do |text|
expect(page).to have_content(text)
end
Then('I should not see {string}') do |text|
expect(page).not_to have_content(text)
end
Then('I should see {string} within {string}') do |text, selector|
within(selector) do
expect(page).to have_content(text)
end
end
Fortgeschrittene Cucumber Techniken
Jetzt wo du die Basics verstehst, lass mich dir ein paar fortgeschrittene Patterns zeigen die ich bei Enterprise Projekten nutze.
Scenario Outlines: Data Driven Testing
Wenn du dasselbe Verhalten mit verschiedenen Inputs testen musst, eliminieren Scenario Outlines die Wiederholung:
# features/pricing_tiers.feature
# Alle verschiedenen Preisstufen testen
# Viel cleaner als 10 separate Scenarios zu schreiben
Feature: Subscription Pricing
As a potential customer
I want to see accurate pricing for different plans
So that I can choose the right subscription
Scenario Outline: Displaying correct prices for each tier
Given I am on the pricing page
When I select the "<plan>" plan
Then I should see the price "<monthly_price>" per month
And I should see the price "<annual_price>" per year
And I should see "<features>" included
Examples:
| plan | monthly_price | annual_price | features |
| Starter | €9.99 | €99.99 | 5 projects, 1 user |
| Pro | €29.99 | €299.99 | 50 projects, 5 users |
| Business | €99.99 | €999.99 | Unlimited projects, 25 users |
| Enterprise | Custom | Custom | Unlimited everything, SLA |
Cucumber führt das Scenario einmal für jede Zeile in der Examples Tabelle aus und ersetzt die Platzhalter. Das ist unglaublich mächtig für das Testen von Business Regeln mit vielen Variationen.
Tags: Tests organisieren und filtern
Tags lassen dich Scenarios kategorisieren und spezifische Subsets ausführen:
@authentication @critical
Feature: User Authentication
@smoke @fast
Scenario: Successful login
# Das läuft in der Smoke Test Suite und Fast Test Suite
Given I am on the login page
When I enter valid credentials
Then I should be logged in
@regression
Scenario: Login with expired password
# Läuft nur in voller Regression Suite
Given my password expired 30 days ago
When I try to log in
Then I should be prompted to change my password
@wip
Scenario: Two factor authentication
# Work in Progress von CI ausgeschlossen
Given I have 2FA enabled
When I enter my password
Then I should be prompted for my 2FA code
Spezifische Tags von der Kommandozeile ausführen:
# Nur Smoke Tests ausführen schnelles Feedback
cucumber --tags @smoke
# Alles außer Work in Progress
cucumber --tags "not @wip"
# Kritische Authentication Tests
cucumber --tags "@critical and @authentication"
Hooks: Setup und Teardown
Hooks lassen dich Code vor oder nach Scenarios ausführen:
# features/support/hooks.rb
# Globales Setup und Teardown
# Hält individuelle Scenarios sauber
# Läuft vor jedem Scenario
Before do
# Übriggebliebene Daten von vorherigen Tests clearen
ActionMailer::Base.deliveries.clear
# Gestubte Services resetten
WebMock.reset!
# Konsistente Zeit setzen für deterministische Tests
Timecop.freeze(Time.zone.parse('2026-04-08 10:00:00'))
end
# Läuft nach jedem Scenario
After do |scenario|
# Zeit wieder freigeben
Timecop.return
# Screenshot machen wenn Scenario fehlgeschlagen hilft beim Debuggen
if scenario.failed?
timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
filename = "tmp/screenshots/failed_#{timestamp}.png"
page.save_screenshot(filename)
puts "Screenshot gespeichert: #{filename}"
end
end
# Läuft vor Scenarios mit @javascript Tag
Before('@javascript') do
# Echten Browser für JS Tests nutzen
Capybara.current_driver = :selenium_chrome_headless
end
After('@javascript') do
# Auf Default Driver zurücksetzen
Capybara.use_default_driver
end
Page Objects: Step Definitions wartbar halten
Für komplexe Seiten nutze das Page Object Pattern um Selektoren zu kapseln:
# features/support/pages/checkout_page.rb
# Kapselt alle Checkout Seiten Interaktionen
# Wenn sich die UI ändert, nur diese Datei updaten
class CheckoutPage
include Capybara::DSL
def visit_page
visit checkout_path
end
def fill_shipping_address(address)
within '#shipping-address' do
fill_in 'Adresszeile 1', with: address[:line1]
fill_in 'Adresszeile 2', with: address[:line2] if address[:line2]
fill_in 'Stadt', with: address[:city]
fill_in 'PLZ', with: address[:postcode]
select address[:country], from: 'Land'
end
end
def fill_payment_details(card)
within '#payment-details' do
fill_in 'Kartennummer', with: card[:number]
fill_in 'Ablaufdatum', with: card[:expiry]
fill_in 'CVC', with: card[:cvc]
fill_in 'Name auf Karte', with: card[:name]
end
end
def apply_discount_code(code)
fill_in 'Rabattcode', with: code
click_button 'Anwenden'
end
def place_order
click_button 'Bestellung aufgeben'
end
def order_total
find('#order-total').text.gsub(/[^\d,]/, '').gsub(',', '.').to_f
end
end
Cucumber als lebende Dokumentation
Das ist vielleicht der am meisten unterschätzte Vorteil von BDD. Deine Feature Dateien sind nicht nur Tests, sie sind Dokumentation die immer akkurat ist weil sie ausgeführt wird.
Überleg mal: wie oft ist die Dokumentation deines Projekts veraltet? Anforderungsdokumente die vor Monaten geschrieben und nie aktualisiert wurden. Wiki Seiten die beschreiben wie Dinge früher funktioniert haben. README Dateien die bestenfalls optimistisch sind.
Mit Cucumber ist die Dokumentation der Test. Wenn die Feature Datei sagt dass Klicken auf "Absenden" eine Email sendet, und der Test besteht, dann sendet Klicken auf "Absenden" definitiv eine Email. Die Dokumentation kann nicht lügen weil sie automatisch verifiziert wird.
Ich strukturiere Feature Dateien für Lesbarkeit:
# features/orders/order_lifecycle.feature
# Diese Datei dokumentiert den kompletten Bestellungs Lebenszyklus
# Lies sie um zu verstehen wie Bestellungen in diesem System funktionieren
@orders @lifecycle
Feature: Order Lifecycle Management
Orders in the system move through several states from creation to completion.
This feature documents and verifies the valid state transitions.
Background:
Given a customer with email "[email protected]"
And they have a verified payment method
And there is a product "Widget Pro" priced at €49.99
# ============================================
# HAPPY PATH: Normale Bestellungsabwicklung
# ============================================
@happy_path
Scenario: Complete order lifecycle
# Kunde gibt Bestellung auf
Given the customer adds "Widget Pro" to their cart
When they complete the checkout process
Then a new order should be created with status "pending"
And the customer should receive an order confirmation email
# Zahlung wird verarbeitet
When the payment is successfully processed
Then the order status should change to "confirmed"
And the inventory for "Widget Pro" should decrease by 1
# Bestellung wird versandt
When the warehouse ships the order
Then the order status should change to "shipped"
And the customer should receive a shipping notification email
# Bestellung wird zugestellt
When the courier marks the order as delivered
Then the order status should change to "delivered"
And the customer should receive a delivery confirmation email
Ein neuer Entwickler der zum Projekt stößt kann diese Datei lesen und genau verstehen wie Bestellungen funktionieren. Er muss nicht durch Code graben oder Fragen stellen. Die Dokumentation ist umfassend, akkurat und immer aktuell.
Häufige Fehler und wie man sie vermeidet
Nach Jahren mit Cucumber habe ich jeden Fehler im Buch gesehen. Hier sind die großen:
Fehler 1: Implementation testen statt Verhalten
Schlecht:
Scenario: Create user record
When I POST to /api/users with JSON {"name": "Dave"}
Then the response status should be 201
And the users table should have 1 row
Gut:
Scenario: Registering a new account
Given I am on the registration page
When I register with the name "Dave"
Then I should see "Welcome, Dave!"
And I should receive a welcome email
Das erste Beispiel testet technische Implementation. Wenn du die API Struktur änderst, bricht der Test obwohl das Verhalten dasselbe ist. Das zweite Beispiel testet was für den User wichtig ist.
Fehler 2: Zu spezifische Steps
Schlecht:
When I click on the button with id "submit-form" and class "btn-primary"
Gut:
When I submit the form
Binde Steps nicht an CSS Selektoren. Wenn sich das Design ändert, brechen alle deine Tests.
Fehler 3: Zu viele Scenarios in einer Datei
Eine Feature Datei mit 50 Scenarios ist schwer zu navigieren und zu warten. Brich sie auf:
features/
orders/
placing_orders.feature
order_cancellation.feature
order_refunds.feature
users/
registration.feature
authentication.feature
Fehler 4: Background nicht nutzen
Wenn jedes Scenario mit denselben 5 Steps beginnt, nutze einen Background.
Fehler 5: Riesige Step Definitions
Schlecht:
When('I complete the entire checkout process') do
# 100 Zeilen Code die alles machen
end
Gut:
When('I complete the checkout process') do
step 'I fill in my shipping address'
step 'I select standard shipping'
step 'I fill in my card details'
step 'I accept the terms and conditions'
step 'I place my order'
end
Komponiere komplexe Steps aus einfacheren.
Der Business Wert: Warum das nicht verhandelbar ist
Lass mich direkt sagen warum ich mich weigere an Projekten ohne Cucumber oder äquivalente BDD Tools zu arbeiten.
Klarheit der Anforderungen: Bevor jemand eine Zeile Code schreibt, ist das Verhalten präzise spezifiziert. "Ich dachte du meintest..." wird unmöglich.
Reduzierte Nacharbeit: Wenn der Test die Spezifikation ist, gibt es keine Lücke zwischen dem was angefragt und dem was gebaut wurde. Features funktionieren beim ersten Mal korrekt.
Furchtloses Refactoring: Mit umfassender BDD Coverage kann ich Code selbstbewusst umstrukturieren. Die Tests sagen mir sofort wenn was kaputtgeht.
Lebende Dokumentation: Neue Teammitglieder verstehen das System indem sie Feature Dateien lesen. Keine veralteten Wikis oder überholten Diagramme.
Stakeholder Kommunikation: Product Manager, Designer und sogar Executives können Feature Dateien lesen und dazu beitragen. Alle sprechen dieselbe Sprache.
Regressions Prävention: Jeder Bug Fix beinhaltet ein Scenario das ihn gefangen hätte. Bugs kommen nicht zurück.
Deployment Selbstvertrauen: Wenn die Cucumber Suite besteht, funktioniert die Software. Deployments werden Routine statt stressig.
Die Projekte die ich früher erwähnt habe, British Army, Volvo Group, Stint.co, Regios.at, Icebreaker.com, sie alle teilen dieses Fundament. Komplexe Systeme mit hohen Einsätzen und null Toleranz für "funktioniert auf meinem Rechner." Cucumber macht sie zuverlässig.
Loslegen: Dein erstes Feature
Wenn du neu bei Cucumber bist, fang klein an:
- Wähl eine User Journey, Login, Checkout, oder was auch immer zentral für deine Anwendung ist
- Schreib die Feature Datei zuerst in klarer Sprache, mach dir keine Sorgen um Step Definitions noch
- Zeig sie einem nicht technischen Stakeholder und frag ob sie Sinn macht
- Implementiere die Step Definitions eine nach der anderen
- Führe das Feature aus und schau zu wie es besteht
- Erweitere auf mehr Features
Innerhalb einer Woche wirst du dich fragen wie du jemals Software ohne es gebaut hast.
Ich nutze Cucumber seit über einem Jahrzehnt quer durch dutzende Projekte. Die Beispiele in diesem Post basieren auf echten Patterns aus Produktionsanwendungen. Die Rechtschreibfehler in Code Kommentaren sind absichtlich, weil echte Entwickler so Kommentare schreiben.
Brauchst du Hilfe beim Etablieren von BDD Praktiken in deinem Projekt? Oder beim Migrieren einer Legacy Codebase zu Cucumber? Ich habe das unzählige Male gemacht und kann dir helfen es richtig zu machen. Lass uns reden.