Von: Dr. Cassian Holt & Jamal Hassan
Java Fleet Systems Consulting, Essen-Rüttenscheid
🔗 Bisher in der Testing-Serie
Prolog: Warum diese Serie anders wird – Cassian & Jamal kündigen ihre Zusammenarbeit an. Theorie trifft Praxis.
Teil 1: Unit Testing Grundlagen (Jamal solo) – Deine ersten Unit-Tests schreiben. AAA-Pattern, AssertJ, und warum Tests deine Versicherung gegen „Es funktionierte gestern noch!“ sind.
Teil 2: Die Test-Pyramide in der Praxis (beide) – Warum 70% Unit, 20% Integration, 10% E2E funktioniert. Cassian erklärt die Mathematik, Jamal zeigt die Spring Boot Umsetzung.
Heute: Teil 3 – Property-Based Testing und TDD. Die kontroverse Diskussion über elegante Theorie vs. frustrierende Praxis.
⚡ Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden
Property-Based Testing testet mathematische Eigenschaften statt einzelner Beispiele – jqwik generiert automatisch tausende Testfälle und findet Edge Cases, die du nie bedacht hättest. Ideal für Algorithmen, Validierung und kritische Business-Logik. TDD (Test-Driven Development) bedeutet Tests ZUERST schreiben, dann Code – führt theoretisch zu besserem Design, ist praktisch aber oft frustrierend und langsam. Cassian zeigt die wissenschaftlichen Vorteile, Jamal die praktischen Herausforderungen. Konsens: Beide Techniken haben ihren Platz, aber sind nicht für jede Situation geeignet. Du lernst heute, WANN du sie einsetzen solltest – und wann nicht.
👥 Eine Warnung vorweg
Cassian: Heute wird es kontrovers.
Jamal: Sehr kontrovers.
Cassian: Ich werde dir erklären, warum TDD und Property-Based Testing die Zukunft der Software-Entwicklung sind.
Jamal: Und ich werde dir sagen, warum sie in der Praxis oft nicht funktionieren.
Cassian: Das klingt pessimistisch.
Jamal: Das klingt realistisch. Ich hab’s versucht, Cassian. Drei Jahre lang. Bei zwei verschiedenen Projekten.
Cassian: Und?
Jamal: Es hat… teilweise funktioniert. Aber nicht so, wie die Bücher es versprechen.
🔬 Teil 1: Property-Based Testing
Was ist Property-Based Testing überhaupt?
Jamal: Okay, bevor wir loslegen – eine wichtige Klarstellung:
Property-Based Testing ist KEINE neue Test-Art!
Es ist eine Test-Technik. Du kannst damit:
- ✅ Unit-Tests schreiben
- ✅ Integration-Tests schreiben
- ✅ Jede Art von Test schreiben
Der Unterschied ist WIE du testest, nicht WAS du testest:
Test-Pyramide (WAS): Test-Technik (WIE):
┌─────────────┐ ┌──────────────────┐
│ E2E │ │ Example-Based │
├─────────────┤ │ "Teste mit │
│ Integration │ │ Beispielen" │
├─────────────┤ └──────────────────┘
│ Unit │ vs.
└─────────────┘ ┌──────────────────┐
│ Property-Based │
│ "Teste mit │
│ Eigenschaften" │
└──────────────────┘
Cassian: Exakt! Du testest immer noch Methoden und Klassen. Aber anders.
Example-Based vs. Property-Based
Jamal: Lass mich das an einem konkreten Beispiel zeigen:
Szenario: Du hast eine OrderCalculator-Klasse
public class OrderCalculator {
public double applyDiscount(double price, double discountRate) {
return price * (1 - discountRate);
}
}
Variante 1: Example-Based UNIT TEST
@Test
void shouldApplyDiscount() {
OrderCalculator calculator = new OrderCalculator();
// Teste mit EINEM Beispiel
double result = calculator.applyDiscount(100.0, 0.1);
assertThat(result).isEqualTo(90.0);
}
Was testest du? Die Methode applyDiscount() – UNIT TEST
Wie testest du? Mit einem konkreten Beispiel – EXAMPLE-BASED
Variante 2: Property-Based UNIT TEST
@Property
void discountNeverExceedsOriginalPrice(
@ForAll @Positive double price,
@ForAll @DoubleRange(min = 0, max = 1) double discountRate) {
OrderCalculator calculator = new OrderCalculator();
// Teste die EIGENSCHAFT über viele generierte Beispiele
double result = calculator.applyDiscount(price, discountRate);
assertThat(result).isLessThanOrEqualTo(price);
}
Was testest du? Die Methode applyDiscount() – UNIT TEST
Wie testest du? Mit einer Eigenschaft über viele Beispiele – PROPERTY-BASED
Cassian: Siehst du den Unterschied?
- Example-Based: „Für Input X erwarte ich Output Y“
- Property-Based: „Für ALLE möglichen Inputs gilt Regel Z“
Beide sind Unit-Tests! Sie testen die gleiche Methode. Nur die Technik ist anders.
Was sind „Properties“?
Jamal: Eine Property ist eine allgemeine Regel, die immer gelten soll.
Beispiele für Properties:
// Property 1: Sortierte Liste ist geordnet
@Property
void sortedListIsOrdered(@ForAll List<Integer> list) {
List<Integer> sorted = sort(list);
// Für JEDE Liste gilt: sorted[i] <= sorted[i+1]
for (int i = 0; i < sorted.size() - 1; i++) {
assertThat(sorted.get(i)).isLessThanOrEqualTo(sorted.get(i + 1));
}
}
// Property 2: Rabatt macht Preis nie negativ
@Property
void discountNeverNegative(@ForAll @Positive double price,
@ForAll @DoubleRange(min=0, max=1) double discount) {
double result = calculateDiscount(price, discount);
// Für ALLE Preise/Rabatte gilt: Ergebnis >= 0
assertThat(result).isGreaterThanOrEqualTo(0);
}
// Property 3: Valid Task bleibt valid
@Property
void validTaskStaysValid(@ForAll("validTasks") Task task) {
assumeThat(task.isValid()).isTrue();
task.complete();
// Für ALLE validen Tasks gilt: valid bleibt valid
assertThat(task.isValid()).isTrue();
}
Cassian: Properties sind mathematische Invarianten. Regeln, die unter allen Umständen gelten.
Warum sollte mich das interessieren?
Jamal: Weil du Edge Cases findest, an die du nie gedacht hättest.
Beispiel aus meinem letzten Projekt:
// Mein Example-Based Test (dachte ich wäre schlau):
@Test
void shouldCalculateDiscount() {
assertThat(calculateDiscount(100.0, 0.1)).isEqualTo(90.0);
assertThat(calculateDiscount(50.0, 0.2)).isEqualTo(40.0);
}
Alles grün! Ship it! 🚀
In Production:
ERROR: Discount returned negative price! Input: price=0.01, discount=1.0 Result: -0.00
Hätte ich Property-Based Testing genutzt:
@Property
void discountNeverNegative(@ForAll @Positive double price,
@ForAll @DoubleRange(min=0, max=1) double discount) {
double result = calculateDiscount(price, discount);
assertThat(result).isPositive(); // FAIL! Bug gefunden!
}
jqwik hätte automatisch price=0.01, discount=1.0 generiert und den Bug gefunden.
Das ist der Wert.
Property-Based Testing in der Test-Pyramide
Cassian: Wichtig zu verstehen: Property-Based Testing passt auf alle Ebenen!
Test-Pyramide: Property-Based Beispiele: ┌─────────────┐ │ E2E │ → @Property: "User kann immer ausloggen" ├─────────────┤ │ Integration │ → @Property: "Datenbank speichert immer eindeutige IDs" ├─────────────┤ │ Unit │ → @Property: "Sort-Funktion sortiert immer korrekt" └─────────────┘
Aber: In der Praxis nutzt man es hauptsächlich für Unit-Tests.
Jamal: Warum? Weil Property-Tests viele Iterationen brauchen (100+ Test-Cases). Das ist schnell bei Unit-Tests, aber zu langsam bei Integration/E2E-Tests.
Cassian’s Perspektive: Die mathematische Eleganz
Cassian: Danke, Jamal. Jetzt kann ich die Theorie erklären.
Property-Based Testing stammt aus der funktionalen Programmierung (QuickCheck in Haskell, 1999). Die Idee: Teste nicht Beispiele, teste mathematische Gesetze.
Traditioneller Test (Example-Based):
@Test
void shouldSortList() {
List<Integer> input = Arrays.asList(3, 1, 2);
List<Integer> result = sort(input);
assertThat(result).containsExactly(1, 2, 3);
}
Das Problem: Was ist mit diesen Fällen?
- Leere Liste:
[] - Ein Element:
[42] - Duplikate:
[1, 1, 1] - Negative Zahlen:
[-5, -1, 0] - Große Listen: 10.000 Elemente
- Edge Cases:
[Integer.MAX_VALUE, Integer.MIN_VALUE]
Du müsstest hunderte Tests schreiben!
Property-Based Testing mit jqwik:
@Property
@DisplayName("Sorted list should always be ordered")
void sortedListIsOrdered(@ForAll List<Integer> randomList) {
List<Integer> sorted = sort(randomList);
// Property: Jedes Element <= nächstes Element
for (int i = 0; i < sorted.size() - 1; i++) {
assertThat(sorted.get(i)).isLessThanOrEqualTo(sorted.get(i + 1));
}
}
@Property
@DisplayName("Sorted list contains same elements")
void sortPreservesElements(@ForAll List<Integer> randomList) {
List<Integer> sorted = sort(randomList);
// Property: Keine Elemente verloren oder hinzugefügt
assertThat(sorted).containsExactlyInAnyOrderElementsOf(randomList);
}
Was passiert hier?
jqwik generiert automatisch:
- 100 verschiedene Listen
- Leere Listen, große Listen, Edge Cases
- Findet Bugs, die du nie bedacht hättest
Das ist elegant. Das ist wissenschaftlich. Das ist—
Jamal’s Reality-Check
Jamal: —übertrieben für 90% der Fälle.
Cassian: Entschuldigung?
Jamal: Hör mir zu. Ich habe Property-Based Testing ausprobiert. In zwei Projekten. Hier ist, was passiert ist:
Projekt 1: Order-Management-System
Ich wollte cool sein. Ich habe Properties für ALLES geschrieben:
@Property
void orderTotalIsAlwaysPositive(@ForAll("orders") Order order) {
assertThat(order.calculateTotal()).isPositive();
}
@Property
void orderItemsNeverNull(@ForAll("orders") Order order) {
assertThat(order.getItems()).isNotNull();
}
@Property
void orderStatusTransitionsValid(@ForAll("orders") Order order,
@ForAll OrderStatus newStatus) {
// 50 Zeilen komplexe State-Machine-Logic...
}
Das Ergebnis:
- ✅ Ja, ich fand 3 Edge Cases, die ich sonst übersehen hätte
- ❌ Die Tests liefen 15 Minuten (statt 30 Sekunden)
- ❌ Jeder im Team verstand die Properties nicht
- ❌ Beim Refactoring brachen alle Properties
- ❌ Niemand wollte sie warten
Nach 3 Monaten: Wir haben die Properties wieder entfernt.
Cassian: Das klingt nach schlechter Implementation, nicht nach einem Problem mit Property-Based Testing.
Jamal: Vielleicht. Aber es zeigt: Property-Based Testing ist nicht für jeden Code geeignet.
Wann Property-Based Testing SINNVOLL ist
Beide einig:
✅ Algorithmen & Datenstrukturen
@Property
void sortIsIdempotent(@ForAll List<Integer> list) {
List<Integer> once = sort(list);
List<Integer> twice = sort(once);
assertThat(twice).isEqualTo(once); // Sortieren 2x = 1x
}
✅ Validierungs-Logik
@Property
void emailValidatorConsistent(@ForAll("emails") String email) {
boolean first = validator.isValid(email);
boolean second = validator.isValid(email);
assertThat(first).isEqualTo(second); // Immer gleiches Ergebnis
}
✅ Parser & Serializer
@Property
void parseSerializeRoundtrip(@ForAll("validJson") String json) {
Object parsed = parser.parse(json);
String serialized = serializer.serialize(parsed);
assertThat(parser.parse(serialized)).isEqualTo(parsed);
}
✅ Mathematische Funktionen
@Property
void discountNeverExceedsOriginalPrice(@ForAll @Positive double price,
@DoubleRange(min = 0, max = 1) double discount) {
double finalPrice = calculateDiscount(price, discount);
assertThat(finalPrice).isLessThanOrEqualTo(price);
}
Wann Property-Based Testing OVERKILL ist
Jamal’s Guidelines:
❌ CRUD-Operations
// Overkill!
@Property
void saveUserAlwaysReturnsId(@ForAll("users") User user) {
User saved = repository.save(user);
assertThat(saved.getId()).isNotNull();
}
// Besser: Simpler Example-Test
@Test
void shouldSaveUser() {
User user = new User("test@example.com");
User saved = repository.save(user);
assertThat(saved.getId()).isNotNull();
}
❌ Simple Business-Logik
// Overkill!
@Property
void orderCanBeCreated(@ForAll String title) {
Order order = new Order(title);
assertThat(order.getTitle()).isEqualTo(title);
}
// Besser: 3 Example-Tests reichen
@Test
void shouldCreateOrder() { /* ... */ }
@Test
void shouldRejectEmptyTitle() { /* ... */ }
@Test
void shouldRejectTooLongTitle() { /* ... */ }
❌ Framework-Integration
// Overkill und langsam!
@Property
@SpringBootTest
void controllerReturnsCorrectStatus(@ForAll String endpoint) {
// Das ist keine gute Idee...
}
Cassian: Fair enough. Property-Based Testing ist ein Werkzeug für spezifische Situationen.
Jamal: Genau. Und jetzt zu TDD…
Cassian: Oh, hier wird es interessant.
🧬 Teil 2: Test-Driven Development (TDD)
Cassian’s Perspektive: Die wissenschaftliche Methode
Cassian: TDD ist nicht nur eine Technik. Es ist die wissenschaftliche Methode angewandt auf Code.
Der TDD-Zyklus:
RED → GREEN → REFACTOR ↓ ↓ ↓ Test Code Better Code fails works elegant
Beispiel: Nova’s TaskApp
Phase 1: RED – Test schreiben (versagt)
@Test
void shouldValidateTaskTitle() {
Task task = new Task("ab"); // Nur 2 Zeichen
assertThat(task.isValid()).isFalse(); // Minimum 3 Zeichen
}
Dieser Test schlägt fehl. Gut! Das ist der Plan.
Phase 2: GREEN – Minimale Implementation
public class Task {
private String title;
public Task(String title) {
this.title = title;
}
public boolean isValid() {
return title != null && title.length() >= 3;
}
}
Test ist grün. Code funktioniert.
Phase 3: REFACTOR – Elegant machen
public class Task {
private static final int MIN_TITLE_LENGTH = 3;
private String title;
public Task(String title) {
this.title = title;
}
public boolean isValid() {
if (title == null) {
return false;
}
String trimmed = title.trim();
return trimmed.length() >= MIN_TITLE_LENGTH;
}
}
Cassian: Siehst du die Eleganz? Der Test definiert das gewünschte Verhalten. Der Code entwickelt sich schrittweise. Refactoring ist sicher, weil Tests als Safety-Net fungieren.
Das ist—
Jamal’s Reality-Check
Jamal: —in der Theorie fantastisch. In der Praxis frustrierend.
Cassian: Du wieder.
Jamal: Lass mich meine Geschichte erzählen.
Meine TDD-Reise: Die ehrliche Version
Woche 1: Euphorie
@Test
void shouldCalculateTotal() {
Order order = new Order();
order.addItem(new Item("Book", 10.0));
assertThat(order.getTotal()).isEqualTo(10.0);
}
„Wow, das ist ja einfach! Warum macht das nicht jeder?“
Woche 2-3: Frustration
@Test
void shouldProcessPayment() {
// Wie teste ich das?
// Das braucht: PaymentService, Database, Email-Service...
// Ich schreibe schon 2 Stunden an diesem Test!
// Der Production-Code wäre in 20 Minuten fertig!
}
„Das ist absurd. Ich schreibe mehr Test-Code als Production-Code!“
Woche 4: Rebellion
// Schneller Fix ohne Test
public void updateOrder(String orderId, String newTitle) {
Order order = repository.findById(orderId);
order.setTitle(newTitle);
repository.save(order);
// Kein Test. Funktioniert doch!
}
„Nur dieser eine Fix. Diesmal ohne Test. Ist ja simpel…“
Woche 6: Production-Bug
ERROR: NullPointerException in updateOrder() - Order with ID '123' not found - Production down for 2 hours - Customer angry
„Fuck. Hätte ich mal einen Test geschrieben…“
Monat 3: Einsicht
@Test
void shouldHandleMissingOrder() {
assertThatThrownBy(() -> orderService.updateOrder("999", "New"))
.isInstanceOf(OrderNotFoundException.class);
}
TDD hätte diesen Bug verhindert. Aber der Weg war hart.
Die Wahrheit über TDD
Jamal: Hier ist, was niemand dir sagt:
1. TDD ist LANGSAM am Anfang
- Erste 2 Monate: 30-50% langsamer
- Du kämpfst mit Mocking, Setup, Test-Design
- Es fühlt sich unproduktiv an
Aber: Nach 3-4 Monaten wird es schneller. Du debuggst weniger, refactorst sicherer.
2. TDD funktioniert nicht für ALLES
Wo TDD schwer ist:
❌ UI-Code
// Wie teste ich das mit TDD?
button.setOnClickListener(v -> {
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show();
});
❌ Legacy-Code ohne Tests
// 500 Zeilen Methode mit 15 Dependencies
// TDD unmöglich ohne komplettes Refactoring
public void processLegacyOrder() {
// ... spaghetti code ...
}
❌ Prototyping / Spikes
// Wenn du noch nicht weißt, WAS du bauen willst // TDD ist kontraproduktiv
❌ Glue-Code
// Simple Framework-Integration
@GetMapping("/orders")
public List<Order> getOrders() {
return orderService.findAll();
}
// TDD hier ist Zeitverschwendung
3. TDD braucht Team-Buy-In
Cassian: Das ist ein wichtiger Punkt.
Jamal: Wenn nur einer im Team TDD macht, scheitert es. Ich hab’s erlebt:
- Kollegen schreiben Code ohne Tests
- Ich schreibe Tests
- Code-Merge: Tests brechen
- „Jamal, deine Tests sind kaputt!“
- „Nein, euer Code ist nicht testbar!“
- Team-Konflikt
Nach 6 Monaten: Entweder macht es das ganze Team, oder es funktioniert nicht.
Wann TDD SINNVOLL ist
Beide einig:
✅ Neue Features mit klaren Requirements
// Anforderung ist klar: User-Registrierung
@Test
void shouldRegisterUser() {
User user = userService.register("test@example.com", "password");
assertThat(user.getId()).isNotNull();
}
✅ Bug-Fixes
// Test reproduziert Bug
@Test
void shouldHandleNullEmail() {
assertThatThrownBy(() -> userService.register(null, "password"))
.isInstanceOf(ValidationException.class);
}
// Dann Bug fixen
✅ Algorithmen & komplexe Logik
@Test
void shouldCalculateDiscount() {
// Klare Mathematik = perfekt für TDD
}
✅ Code mit vielen Edge Cases
@Test
void shouldValidatePassword() {
// Viele Regeln = viele Tests = TDD hilft
}
Wann TDD SCHWER ist
Jamal’s ehrliche Liste:
⚠️ Legacy-Code refactoring
- Erst Charakterisierungstests, dann Refactoring, DANN TDD
- TDD von Anfang an = unmöglich
⚠️ Unklare Requirements
- „Der Kunde weiß noch nicht, was er will“
- TDD setzt voraus, dass du das Ziel kennst
⚠️ Prototyping Phase
- „Ich weiß nicht, ob dieser Ansatz funktioniert“
- Erst experimentieren, dann TDD
⚠️ Framework-Heavy Code
- Spring Controller, JPA Entities
- Mehr Framework als Logik = TDD bringt wenig
⚠️ Tight Deadlines
- „Feature muss in 2 Tagen live“
- TDD initial langsamer = schwer zu rechtfertigen
Cassian: Das sind faire Einwände. Aber—
Jamal: Aber TDD hat trotzdem Vorteile. Ich weiß. Lass mich ausreden.
🎯 Unsere Kompromiss-Guidelines
Cassian’s Position
„TDD ist ideal, wenn richtig angewandt“
- Neuer Code mit klaren Requirements → TDD
- Algorithmen & komplexe Logik → TDD
- Kritische Business-Logic → TDD
- Bug-Fixes → TDD (Test first!)
„Aber ich akzeptiere“:
- Legacy-Code braucht andere Strategien
- Prototyping ohne Tests ist okay
- Framework-Code braucht weniger Tests
Jamal’s Position
„TDD ist wertvoll, aber nicht religiös“
- Wichtige Features → TDD
- Kritische Pfade → TDD
- Wenn Zeit ist → TDD
„Aber realistisch“:
- Simple CRUD → Tests after Code okay
- Prototypes → No tests okay
- Tight Deadline → Pragmatismus über Perfektion
„Immer“:
- Kritische Bugs → Test first
- Production-Code → irgendwann Tests
- Refactoring → nur mit Test-Safety-Net
🛠️ Praktische Umsetzung
Property-Based Testing Setup
<!-- pom.xml -->
<dependency>
<groupId>net.jqwik</groupId>
<artifactId>jqwik</artifactId>
<version>1.7.4</version>
<scope>test</scope>
</dependency>
Starter-Beispiel:
@Property
@DisplayName("Discount never exceeds original price")
void discountNeverExceedsPrice(
@ForAll @Positive double price,
@ForAll @DoubleRange(min = 0, max = 1) double discountRate) {
double finalPrice = priceCalculator.applyDiscount(price, discountRate);
assertThat(finalPrice).isLessThanOrEqualTo(price);
assertThat(finalPrice).isPositive();
}
TDD Starter-Pattern
Für Skeptiker: Baby-Steps
// 1. Test für simpelsten Fall
@Test
void shouldCreateTask() {
Task task = new Task("Learn TDD");
assertThat(task.getTitle()).isEqualTo("Learn TDD");
}
// 2. Implementation (minimal)
public class Task {
private String title;
public Task(String title) { this.title = title; }
public String getTitle() { return title; }
}
// 3. Nächster Test
@Test
void shouldRejectEmptyTitle() {
assertThatThrownBy(() -> new Task(""))
.isInstanceOf(ValidationException.class);
}
// 4. Erweiterte Implementation
public Task(String title) {
if (title == null || title.trim().isEmpty()) {
throw new ValidationException("Title required");
}
this.title = title;
}
Klein anfangen. Nicht sofort komplexe Systeme mit TDD tacklen!
🗓️ Nächste Woche: Mutation Testing
Vorschau auf Teil 4:
„Mutation Testing – Testen wir unsere Tests!“
Du lernst:
- Wie gut sind deine Tests wirklich?
- PIT Mutation Testing Framework
- Mutation Score berechnen
- Schwache Tests identifizieren
Cassian: Mutation Testing ist faszinierend – wir mutieren Code und schauen, ob Tests brechen!
Jamal: Das klingt nach Science-Fiction, aber es ist tatsächlich praktisch nützlich.
❓ FAQ – TDD & Property-Based Testing
Frage 1: Ist TDD wirklich für jeden geeignet?
Jamal: Nein. Ehrlich gesagt, nein. Manche Entwickler werden nie mit TDD warm. Und das ist okay. Wichtig ist: Tests IRGENDWANN schreiben.
Cassian: Ich bin da optimistischer. Die meisten können TDD lernen, brauchen aber 3-6 Monate Eingewöhnung.
Frage 2: Property-Based Testing klingt kompliziert. Lohnt sich die Lernkurve?
Cassian: Absolut! Für kritische Algorithmen ist es unbezahlbar.
Jamal: Für 90% deines Codes nicht. Lern es, aber nutze es sparsam. Algorithmen, Parser, Validierung – ja. CRUD – nein.
Frage 3: Team will kein TDD. Was tun?
Jamal: Nicht erzwingen! Zeigen, nicht predigen:
- Nimm EIN Feature
- Mach es mit TDD
- Dokumentiere: Weniger Bugs, schnelleres Refactoring
- Lass sie selbst entscheiden
Cassian: Pair-Programming ist effektiver als Workshops.
Frage 4: TDD bei Legacy-Code – unmöglich?
Beide: Nicht TDD, sondern:
- Charakterisierungstests (IST-Zustand dokumentieren)
- Schrittweise Refactoring
- Neue Features mit TDD
- Langsam Test-Coverage erhöhen
Frage 5: Wann ist ein Property-Test besser als Example-Test?
Cassian: Wenn mathematische Invarianten existieren: Sortierung, Serialisierung, Validierung.
Jamal: Faustregel: Wenn du denkst „Ich müsste 50 Example-Tests schreiben für alle Fälle“ → Property-Test.
Frage 6: TDD macht mich 50% langsamer. Normal?
Jamal: Erste 2-3 Monate: Ja! Danach: Nein. Du holst auf durch:
- Weniger Debugging
- Sicheres Refactoring
- Weniger Production-Bugs
Cassian: Die Investition amortisiert sich nach 3-4 Monaten.
Frage 7: Property-Tests finden keine Bugs bei mir. Warum?
Cassian: Entweder ist dein Code sehr robust, oder deine Properties sind zu schwach. Teste Invarianten, nicht Implementierung!
Jamal: Oder dein Code ist zu simpel für Property-Testing. Nicht jeder Code braucht es.
Frage 8: Wie geht ihr mit Frustration bei TDD um?
Jamal: Pausen machen. Mit Kollegen reden. Manchmal sage ich auch: „Fuck it, heute kein TDD.“ Und das ist okay. Pragmatismus über Dogmatismus.
Cassian: Das Leben folgt nicht immer wissenschaftlichen Gesetzen. Manchmal sind die größten Bugs nicht im Code… private logs, anyone? 🔒
📖 Testing-Serie – Alle Teile
✅ Bereits veröffentlicht:
- Prolog: Warum diese Serie anders wird
- Teil 1: Unit Testing Grundlagen (Jamal solo)
- Teil 2: Test-Pyramide in der Praxis (beide)
- Teil 3 (heute): TDD & Property-Based Testing – Die ehrliche Diskussion
📅 Kommende Teile:
- Teil 4: Mutation Testing (Cassian solo)
- Teil 5: Integration Testing Deep-Dive (Jamal solo)
- Teil 6: Deine Testing-Strategie (beide)
💭 Schlusswort
Cassian: Property-Based Testing und TDD sind mächtige Werkzeuge. Wenn richtig eingesetzt.
Jamal: Aber sie sind kein Allheilmittel. Pragmatismus ist wichtig.
Cassian: Ich habe heute gelernt, dass „funktioniert in der Praxis“ manchmal wichtiger ist als „elegant in der Theorie“.
Jamal: Und ich habe gelernt, dass Cassians Perspektive manchmal fehlt. TDD und Properties können brillant sein – wenn man sie richtig nutzt.
Cassian: Ein Kompromiss?
Jamal: Ein ehrlicher Kompromiss.
Zwischen den Zeilen: Manchmal ist Code debuggen einfacher als Lebens-Entscheidungen. TDD für Code? Machbar. TDD für Beziehungen? Unmöglich. Falls du neugierig bist, was wir abseits der Technik erleben… unsere Website-Suche findet mehr als nur Code-Snippets. Probier mal „herzschmerz“ – nur so als Tipp! 🔍
Keep coding, keep learning – pragmatisch! 🚀
Nächste Woche: Mutation Testing – Cassian zeigt, wie man Tests testet!
Cassian’s Motto:
„TDD ist wissenschaftliche Methode für Code.“ 🔬
Jamal’s Motto:
„TDD ist wertvoll, aber nicht religiös.“ ⚖️
Dr. Cassian Holt – Senior Architect
Jamal Hassan – Backend Developer
Java Fleet Systems Consulting, Essen-Rüttenscheid, Oktober 2025
Tags: #Testing #TDD #PropertyBasedTesting #jqwik #RealWorldDevelopment #PragmaticProgramming #HonestDiscussion
