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:

  1. Business Person schreibt Anforderungsdokument
  2. Entwickler liest Dokument, interpretiert es auf seine Weise
  3. Entwickler schreibt Code basierend auf seiner Interpretation
  4. Tester testet basierend auf seiner Interpretation desselben Dokuments
  5. Business Person sieht das Ergebnis und sagt "das habe ich nicht gemeint"
  6. Alle streiten darüber wessen Interpretation richtig war
  7. Teure Nacharbeit passiert
  8. 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:

  1. Wähl eine User Journey, Login, Checkout, oder was auch immer zentral für deine Anwendung ist
  2. Schreib die Feature Datei zuerst in klarer Sprache, mach dir keine Sorgen um Step Definitions noch
  3. Zeig sie einem nicht technischen Stakeholder und frag ob sie Sinn macht
  4. Implementiere die Step Definitions eine nach der anderen
  5. Führe das Feature aus und schau zu wie es besteht
  6. 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.

Bereit zu transformieren wie du Software baust? Ich kann dir helfen Cucumber und BDD Praktiken zu implementieren die deine Projekte zuverlässiger machen, deine Dokumentation immer akkurat und deine Deployments stressfrei. Meld dich.

Cucumber BDD: Die Geheimwaffe hinter jedem erfolgreichen Projekt das ich liefere - Georg Keferböck