TDD und BDD in Ruby on Rails: Ohne Angst deployen, und wie KI das Spiel verändert
Lass mich dir von der teuersten Codezeile erzählen, die ich je gesehen habe. Sie war nicht kompliziert. Sie war nicht clever. Es war ein Rundungsfehler in einer Währungsumrechnungsfunktion. Drei Dezimalstellen statt zwei. In der Testumgebung hat es niemand bemerkt. In der Produktion, bei der Verarbeitung tausender Transaktionen pro Tag für einen Fintech-Kunden, entstand eine Diskrepanz, die zwei Wochen brauchte, um sie aufzulösen. Die Korrektur dauerte vier Minuten. Die Aufräumarbeiten dauerten vierhundert Stunden. Diese Erfahrung hat für immer verändert, wie ich Software schreibe. Nicht weil ich gelernt habe, vorsichtiger zu sein (obwohl das auch), sondern weil ich gelernt habe, dass die Art, wie die meiste Software gebaut wird, grundlegend kaputt ist. Code schreiben, dann testen, dann Bugs fixen, dann nochmal testen, dann mehr Bugs finden, dann ausliefern und beten. Das ist keine Entwicklungsmethodik. Das ist Glücksspiel. Es gibt einen besseren Weg. Eigentlich gibt es zwei bessere Wege: Test Driven Development (TDD) und Behaviour Driven Development (BDD). Sie stellen den gesamten Prozess auf den Kopf. Statt Code zu schreiben und zu hoffen, dass er funktioniert, schreibst du zuerst die Tests und dann Code, der sie bestehen lässt. Es klingt kontraintuitiv. Es fühlt sich anfangs langsam an. Und es wird dir Tausende von Stunden und potenziell Millionen an Kosten über die Lebensdauer eines Projekts sparen. Ich nutze beide Ansätze jeden Tag beim Bau von [Auto-Prammer.at](https://auto-prammer.at) auf Ruby on Rails und Solidus, und bei der Entwicklung des Fintech-SaaS für [Regios](https://regios.at) mit Modulen von [GrowCentric.ai](https://growcentric.ai). Lass mich dir genau zeigen, wie das funktioniert, warum es wichtig ist und wie KI es noch leistungsfähiger macht.
Der alte Weg: Erst coden, dann beten
Jahrzehntelang sah der vorherrschende Ansatz zum Bau von Software ungefähr so aus. Ein Product Manager schreibt eine Spezifikation. Entwickler lesen sie (manchmal). Entwickler schreiben Code. Jemand testet ihn, meistens manuell, meistens unter Zeitdruck. Bugs werden gefunden. Bugs werden gefixt. Neue Bugs werden durch die Fixes eingeführt. Mehr Testing. Mehr Fixen. Irgendwann sagt jemand "gut genug" und es wird ausgeliefert.
Die Zahlen erzählen die Geschichte, wie schlecht dieser Ansatz scheitert. Mangelnde Softwarequalität kostete allein die USA geschätzte 2,41 Billionen Dollar im Jahr 2022, laut dem Consortium for Information and Software Quality (CISQ). Entwickler führen typischerweise 100 bis 150 Fehler pro tausend Codezeilen ein. Ein Viertel aller Entwickler verbringt die Hälfte ihrer Arbeitszeit damit, Bugs zu fixen statt Features zu bauen. Und der Clou: Einen Bug in der Produktion zu fixen kostet bis zu 100-mal mehr als ihn in der Entwicklung zu fangen.
Manche dieser Bugs waren katastrophal. 2012 verlor Knight Capital Group 440 Millionen Dollar in 45 Minuten wegen eines fehlerhaften Trading-Algorithmus, der es in die Produktion geschafft hatte. 2015 stürzte ein Airbus A400M während eines Testflugs ab und tötete vier Crew-Mitglieder, wegen Softwarefehlern im Motorsteuerungssystem. Im Juli 2024 verursachte ein fehlerhaftes CrowdStrike-Update einen weltweiten IT-Ausfall, der Airlines, Banken und Gesundheitsdienste betraf.
Das sind keine Einzelfälle. Sie sind die logische Konsequenz einer Entwicklungsmethodik, die Testing als Nachgedanke behandelt.
TDD: Zuerst den Test schreiben
Test Driven Development dreht den Spieß komplett um. Statt Code zu schreiben und dann zu testen, schreibst du zuerst den Test, schaust zu wie er fehlschlägt, schreibst den minimalen Code, der nötig ist, damit er besteht, und refaktorisierst dann. Das ist der berühmte Red-Green-Refactor-Zyklus.
Red: Schreib einen Test, der das gewünschte Verhalten beschreibt. Führe ihn aus. Er schlägt fehl (weil der Code noch nicht existiert). Das ist dein fehlgeschlagener Test, dein rotes Licht.
Green: Schreib den einfachstmöglichen Code, der den Test bestehen lässt. Nicht mehr. Führe den Test erneut aus. Er besteht. Grünes Licht.
Refactor: Jetzt räum den Code auf. Verbessere die Implementierung. Mach sie elegant. Führe die Tests erneut aus. Immer noch grün? Gut. Weiter.
Dieser Zyklus ist bewusst klein. Du schreibst nicht ein ganzes Feature und testest es dann. Du schreibst jeweils ein winziges Verhalten. Jeder Zyklus dauert vielleicht fünf Minuten. Über einen Tag machst du Dutzende dieser Zyklen, und am Ende hast du ein Feature, das korrekt funktioniert, dokumentiert durch eine umfassende Test-Suite, die es beweist.
So sieht das in der Praxis mit RSpec in einer Rails-Anwendung aus. Sagen wir, ich baue einen Währungsumrechnungsservice für Regios, der regionale Digitalwährungen handhabt.
# Schritt 1: RED - Zuerst den Test schreiben
RSpec.describe CurrencyConverter do
describe '#convert' do
it 'rechnet EUR in Regios zum aktuellen Wechselkurs um' do
converter = CurrencyConverter.new(rate: 1.0)
result = converter.convert(amount: 100.00, from: 'EUR', to: 'REGIOS')
expect(result).to eq(100.00)
end
it 'rundet auf genau zwei Dezimalstellen' do
converter = CurrencyConverter.new(rate: 1.0735)
result = converter.convert(amount: 33.33, from: 'EUR', to: 'REGIOS')
expect(result).to eq(35.78)
end
end
end
Dieser Test schlägt fehl, weil CurrencyConverter noch nicht existiert. Das ist der Punkt. Jetzt schreibe ich die Implementierung:
# Schritt 2: GREEN - Minimalen Code zum Bestehen schreiben
class CurrencyConverter
def initialize(rate:)
@rate = rate
end
def convert(amount:, from:, to:)
(amount * @rate).round(2)
end
end
Tests bestehen. Grün. Jetzt kann ich refaktorisieren, mehr Tests für Randfälle hinzufügen (negative Beträge, Null-Kurse, nicht unterstützte Währungen) und das Feature schrittweise aufbauen. Und dieser Rundungstest? Das ist genau die Art von Test, die den Drei-Dezimalstellen-Bug aus meiner Einleitung gefangen hätte. TDD findet nicht nur Bugs. Es verhindert, dass sie überhaupt entstehen.
BDD: Testen aus Nutzerperspektive
Behaviour Driven Development geht einen Schritt weiter als TDD. Wo TDD sich auf das Testen einzelner Code-Einheiten konzentriert (gibt diese Methode den richtigen Wert zurück?), konzentriert sich BDD auf das Testen des Systemverhaltens aus der Nutzerperspektive (kann der Nutzer diesen Workflow erfolgreich abschließen?).
BDD wurde 2003 von Dan North als Antwort auf die Verwirrung geschaffen, die viele Entwickler bei TDD empfanden. Die Terminologie von TDD (Tests, Assertions, Units) ist entwicklerzentriert. BDD formuliert alles in Verhaltens-Begriffen um, in einer Sprache, die auch Business-Stakeholder, Product Manager und Designer verstehen können.
Das Ergebnis sind Tests, die nahe an Klartext geschrieben sind, im Given/When/Then-Format. So sieht das mit Cucumber für Auto-Prammer.at aus, meinen Automobil-Marktplatz auf Rails und Solidus:
Feature: Fahrzeugsuche
Als potenzieller Autokäufer
Möchte ich Fahrzeuge nach Marke, Modell und Preisbereich suchen
Damit ich ein Auto finde, das in mein Budget passt
Scenario: Suche nach einer bestimmten Marke und Modell
Given es gibt 10 BMW 3er Inserate
And es gibt 5 Audi A4 Inserate
When ich nach "BMW 3er" suche
Then sollte ich 10 Ergebnisse sehen
And ich sollte keine Audi-Inserate sehen
Scenario: Filtern nach Preisbereich
Given es gibt einen BMW 320i für 25000 Euro
And es gibt einen BMW 330i für 45000 Euro
When ich mit einem Höchstpreis von 30000 Euro suche
Then sollte ich den BMW 320i sehen
And ich sollte den BMW 330i nicht sehen
Lies das laut vor. Ein Product Manager kann es verstehen. Ein Designer kann es verstehen. Dein nicht-technischer Mitgründer kann es verstehen. Das ist die Kraft von BDD. Die Tests dienen als lebendige Dokumentation, wie sich das System verhalten soll, geschrieben in einer Sprache, die jeder im Team lesen kann.
RSpec vs Minitest vs Cucumber: Die Rails-Testing-Dreifaltigkeit
Ruby on Rails hat eines der reichhaltigsten Testing-Ökosysteme aller Web-Frameworks. Rails war das erste große Framework, das von Tag eins eine Testing-Integration hatte. Aber welches Framework solltest du verwenden?
RSpec ist das dominierende Testing-Framework in der Rails-Welt. Laut einer Stack Overflow Umfrage 2024 wird RSpec von über 59 % der Ruby-on-Rails-Entwickler bevorzugt. Es verwendet eine beschreibende, BDD-inspirierte Syntax mit describe, context und it-Blöcken, die sich wie Spezifikationen lesen. RSpec ist ideal für mittlere bis große Projekte, für Teams, die BDD praktizieren, und für Codebases, in denen Test-Lesbarkeit wichtig ist.
Minitest wird mit Ruby und Rails standardmäßig ausgeliefert. Es ist leichtgewichtig, schnell und fühlt sich an wie pures Ruby. Es gibt keine DSL zu lernen; wenn du Ruby kannst, kannst du Minitest. Es unterstützt sowohl TDD-Stil (assert_equal) als auch einen Spec-Stil ähnlich wie RSpec. Minitest ist ideal für kleinere Projekte, für Entwickler, die Einfachheit schätzen, und für Situationen, in denen Testgeschwindigkeit kritisch ist.
Cucumber ist das BDD-Tool, das Gherkin-Syntax (Given/When/Then) für Akzeptanztests verwendet. Es überbrückt die Lücke zwischen technischen und nicht-technischen Stakeholdern, indem Tests in Klartext geschrieben werden. Cucumber ist ideal für Akzeptanztests, für Projekte mit nicht-technischen Stakeholdern und zur Dokumentation von Geschäftsanforderungen als ausführbare Spezifikationen.
Mein ehrlicher Vergleich:
Lesbarkeit: RSpec gewinnt. Seine beschreibenden Blöcke erzeugen Testdateien, die sich wie Dokumentation lesen. Minitest ist sauber, aber knapp. Cucumber ist für Nicht-Entwickler am lesbarsten.
Geschwindigkeit: Minitest gewinnt deutlich in Roh-Benchmarks. RSpec hat mehr Overhead. Cucumber ist am langsamsten, weil es Gherkin parst und durch Step-Definitionen läuft. In der Praxis ist der Geschwindigkeitsunterschied zwischen RSpec und Minitest für die meisten realen Anwendungen marginal (oft unter 10 %).
Ökosystem: RSpec gewinnt. Das Ökosystem an Matchern, Plugins und Integrationen (shoulda-matchers, factory_bot, capybara, vcr, webmock) ist enorm.
Lernkurve: Minitest gewinnt. Es ist einfach Ruby. RSpec führt eine DSL ein, die Zeit zum Lernen braucht. Cucumber fügt Gherkin-Syntax obendrauf.
Meine Empfehlung: Nutze RSpec für Unit- und Integrationstests, und Cucumber für kritische User-Journey-Akzeptanztests. Das ist der Ansatz, den ich sowohl auf Auto-Prammer.at als auch auf der Regios-SaaS-Plattform verwende.
Branchen, in denen Bugs keine Option sind
Nicht alle Bugs sind gleich. Ein verschobener Button auf einer Marketing-Seite ist ärgerlich. Ein Rundungsfehler in einer Finanztransaktion ist ein Compliance-Verstoß. Ein Softwarefehler in einem Medizingerät kann jemanden töten.
In bestimmten Branchen ist umfassendes Testing kein Nice-to-have. Es ist eine gesetzliche und ethische Pflicht.
Fintech und Bankwesen. Finanzsoftware unterliegt strengen Regulierungen einschließlich PSD2, Geldwäsche-Richtlinien und DSGVO. Jede Transaktion muss centgenau sein. Jeder Audit-Trail muss vollständig sein. Deshalb umfasst meine Arbeit mit Regios erschöpfende Test-Suites, die jede Finanzberechnung, jeden Transaktionsstatus und jeden regulatorischen Berichtspfad abdecken.
Gesundheitswesen. Medizinsoftware wird durch Rahmenwerke wie die EU-Medizinprodukteverordnung (MDR) reguliert. Software, die Diagnosegeräte steuert oder Medikamentendosierungen berechnet, muss praktisch fehlerfrei sein.
Luftfahrt. Avionik-Software folgt Standards wie DO-178C, die formale Verifikation und mehrstufiges Testing verlangen. In der Luftfahrt tötet ungetestete Software buchstäblich Menschen.
eCommerce. Ein defekter Checkout-Flow während einer Sale-Aktion kann Millionen an entgangenem Umsatz kosten. Und mit dem Cyber Resilience Act, der jetzt Secure-by-Design-Praktiken verlangt, wird Testing auch für eCommerce-Plattformen zur regulatorischen Pflicht.
Der kritische Pfad: Was vor jedem Release getestet werden muss
Du kannst nicht alles testen. Die Kunst des effektiven Testings ist es, den kritischen Pfad zu identifizieren: die Funktionalitäten, die absolut korrekt funktionieren müssen.
Für jede Webanwendung umfasst der kritische Pfad typischerweise: Authentifizierung und Autorisierung, Kern-Geschäftslogik, Zahlungsabwicklung, Datenintegrität, regulatorische Compliance-Features und API-Verträge.
Jeder einzelne dieser Punkte muss durch automatisierte Tests abgedeckt sein, die vor jedem einzelnen Deployment laufen. Keine Ausnahmen. Kein "wir testen es diesmal manuell." Die CI/CD-Pipeline sollte der Gatekeeper sein: Wenn ein kritischer Pfad-Test fehlschlägt, stoppt das Deployment.
Das Regios-Beispiel: Testing eines Fintech-SaaS unter DSGVO
Regios ist ein Fintech-Kunde, mit dem ich an einer regionalen Digitalwährungsplattform in Österreich arbeite. Die Plattform verarbeitet echtes Geld, echte Transaktionen und echte personenbezogene Daten unter einigen der strengsten Datenschutzregulierungen der Welt.
Die SaaS-Plattform nutzt Module von GrowCentric.ai, meinem kommenden SaaS-Produkt (öffentlicher Launch im Juni 2026), das die Analytics- und Kampagnenoptimierungs-Engine bereitstellt.
Die kritische Test-Suite umfasst DSGVO-Compliance-Tests (Datenexport, Recht auf Löschung, Consent-Management), Transaktionsintegritäts-Tests (atomare Überweisungen, Rollback bei Fehlern, Verhinderung negativer Salden) und Audit-Trail-Tests (Protokollierung jeder signifikanten Aktion mit Akteur, Aktion und Zeitstempel).
Diese Tests laufen bei jedem Pull Request, jedem Merge auf Main und vor jedem Deployment. Die CI-Pipeline blockiert das Deployment, wenn ein Test fehlschlägt. Es gibt keinen manuellen Override. Im Fintech liefert man keinen ungetesteten Code aus. Niemals.
Das Auto-Prammer.at-Beispiel: Testing einer eCommerce-Plattform auf Solidus
Die Teststrategie für Auto-Prammer.at hat einen anderen Charakter, ist aber gleichermaßen rigoros. Auto-Prammer ist eine Autovermietungs-, Werkstatt- und Händler-Webanwendung, gebaut auf Ruby on Rails mit Solidus für die eCommerce-Komponenten.
Der kritische Pfad umfasst Fahrzeugsuche und -filterung, Buchungs- und Reservierungsflows, Zahlungsabwicklung und das Admin-Interface, das Händler und Werkstattbetreiber zur Verwaltung ihrer Inserate nutzen.
Beachte, wie BDD-Szenarien die vollständige User-Journey beschreiben, während RSpec-Tests in die präzisen Berechnungen eintauchen. Sie arbeiten zusammen. Der Cucumber-Test sagt dir, ob sich das System aus Nutzerperspektive korrekt verhält. Der RSpec-Test sagt dir genau, wo etwas schiefgeht, wenn eine Berechnung bricht.
KI nutzen, um dein Testing zu beschleunigen (ohne die Kontrolle zu verlieren)
Jetzt wird es richtig interessant. Tests zu schreiben ist essentiell, aber auch mühsam. Eine umfassende Test-Suite für eine Rails-Anwendung kann Hunderte oder Tausende einzelner Testfälle umfassen.
Hier glänzen KI-Tools wie Claude wirklich. Aber, und das ist ein kritisches Aber, es gibt einen richtigen und einen falschen Weg, KI fürs Testing zu nutzen.
Der falsche Weg: "Hey KI, schreib Tests für meine Anwendung."
Wenn du deinen Code in Claude einfügst und sagst "schreib Tests dafür," bekommst du Tests. Sie bestehen vielleicht sogar. Aber sie werden wahrscheinlich oberflächlich sein, domänenspezifische Randfälle verpassen, Implementierungsdetails statt Verhalten testen und dir ein falsches Sicherheitsgefühl geben. KI-generierte Tests, die du nicht geprüft hast, sind schlimmer als keine Tests, weil sie dich glauben lassen, dass du abgedeckt bist, wenn du es nicht bist.
KI versteht deine Geschäftsdomäne nicht. Sie weiß nicht, dass Regios den Randfall behandeln muss, wenn ein Nutzer versucht, Regios am Wochenende zurück in EUR umzutauschen, wenn der Wechselkursdienst nicht verfügbar ist. Sie weiß nicht, dass Auto-Prammer das Szenario behandeln muss, wenn die Hauptuntersuchung eines Fahrzeugs während einer Mietperiode abläuft.
Der richtige Weg: Menschlich gesteuertes, KI-unterstütztes Testing.
Schritt 1: Denke die Teststrategie selbst durch. Bevor du KI einbeziehst, entscheide, was getestet werden muss und warum. Das ist Menschenarbeit.
Schritt 2: Nutze KI, um Randfälle zu entdecken, an die du nicht gedacht hast. Hier ist Claude wirklich brillant. Präsentiere deine Geschäftslogik und frage: "Welche Randfälle sollte ich für diesen Währungsumrechnungsservice berücksichtigen?" Claude wird Szenarien vorschlagen wie Floating-Point-Präzisionsfehler, Kurse die sich während einer Transaktion ändern, gleichzeitige Umrechnungen, Zeitzonen-Probleme und mehr.
Schritt 3: Nutze KI zum Generieren von Test-Scaffolding. Wenn du weißt, was zu testen ist, lass Claude das Boilerplate generieren. Prüfe jeden einzelnen Test. Modifiziere sie. Lösche die, die keinen Sinn ergeben.
Schritt 4: Niemals blind vertrauen, immer verifizieren. Jeder KI-generierte Test sollte als erster Entwurf behandelt werden. Führe ihn aus. Lies ihn Zeile für Zeile. Frage dich: Verifiziert dieser Test tatsächlich, was ich denke?
Meine persönliche Regel: Wenn ich nicht in einem Satz erklären kann, was ein Test tut und warum er wichtig ist, wird er umgeschrieben oder gelöscht.
Schritt 5: Nutze KI für Test-Review und -Verbesserung. Nach dem Schreiben deiner Tests frage Claude: "Gibt es logische Fehler in diesen Tests? Sind Tests redundant? Sind Assertions zu locker?"
Das Best-Practice-Framework: KI-gestütztes Testing richtig gemacht
Du besitzt die Strategie. Der Mensch entscheidet, was getestet wird, warum, und was ein bestandener Test ist. KI trifft diese Entscheidungen nie.
KI entdeckt die Unbekannten. Nutze KI zum Brainstorming von Randfällen, zum Vorschlagen von Grenzwertbedingungen und zum Identifizieren übersehener Szenarien.
KI generiert das Scaffolding. Lass KI das Boilerplate schreiben, die Factory-Definitionen, den repetitiven Setup-Code.
Du prüfst alles. Jeder Test wird gelesen, verstanden und von einem Menschen freigegeben.
KI liefert eine zweite Meinung. Nach dem Schreiben und Prüfen deiner Tests, lass KI sie kritisieren.
Die Suite ist deine Verantwortung. Wenn ein Test fehlschlägt, musst du verstehen warum. KI kann dir beim Schreiben von Tests helfen, aber die Test-Suite ist dein Produkt, deine Verantwortung und dein Sicherheitsnetz.
Dieses Framework hat sich sowohl bei Auto-Prammer.at als auch beim Regios-SaaS bewährt. Für Auto-Prammer erreichte ich mit diesem Ansatz umfassende Testabdeckung über Fahrzeugsuche, Buchung und Zahlungsflows in etwa der Hälfte der Zeit, die es gebraucht hätte, jeden Test manuell zu schreiben. Wichtiger noch, die KI-gestützte Randfall-Entdeckung fing drei Szenarien auf, die echte Produktionsfehler gewesen wären: ein Datumsberechnungsfehler bei Buchungen über die Sommerzeitumstellung, eine Race Condition bei der Verfügbarkeitsprüfung für gleichzeitige Buchungen und ein Solidus-Checkout-Randfall, bei dem das Anwenden eines Rabattcodes nach dem Hinzufügen eines Servicepakets ein falsches Gesamtergebnis erzeugte.
Fazit: Testing ist keine Steuer, es ist eine Superkraft
Ich baue Software seit lang genug, um mich daran zu erinnern, als Testing als optional galt. Als "es funktioniert auf meinem Rechner" ein akzeptables Deployment-Kriterium war. Als Bugs ausliefern einfach Teil des Prozesses war.
Diese Ära ist vorbei. Die Einsätze sind zu hoch, die Regulierungen zu streng, und die Tools zu gut, als dass irgendjemand es rechtfertigen könnte, ungetesteten Code auszuliefern. TDD und BDD sind nicht nur Methoden. Sie sind eine fundamental andere Art, über Software nachzudenken.
Das Testing-Ökosystem in Ruby on Rails, mit RSpec, Minitest und Cucumber, gibt dir alles, was du brauchst, um das auf jeder Ebene deiner Anwendung umzusetzen. Und KI-Tools wie Claude machen die mühsamsten Teile des Testings dramatisch schneller, ohne das menschliche Urteilsvermögen zu opfern, das Tests wirklich wertvoll macht.
Ob du eine Fintech-Plattform unter DSGVO-Prüfung wie Regios baust, einen Automobil-Marktplatz auf Solidus wie Auto-Prammer.at oder ein SaaS-Produkt wie GrowCentric.ai, das auf den öffentlichen Launch zusteuert, die Investition in Testing zahlt sich vielfach aus. Weniger Bugs, schnellere Entwicklung, mehr Vertrauen und die Fähigkeit, jederzeit zu deployen, im Wissen, dass deine Test-Suite hinter dir steht.
Und wenn du Hilfe beim Aufbau dieser Test-Suite brauchst, beim Einrichten deiner CI/CD-Pipeline oder beim Implementieren von TDD und BDD in deinem Rails-Projekt, genau das ist mein Job. Lass uns reden.