Start / Seminare / Java Testing mit JUnit, Mockito und Testcontainers
Modul
Gute Testarchitektur statt Mocking-Überlastung
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
Gute Testarchitektur statt Mocking-Überlastung
0:00 Dies ist das wichtigste Modul des zweiten Tages, und es dreht die Perspektive um. Bisher haben wir gefragt: Wie ersetze ich eine Abhängigkeit? Jetzt fragen wir: Was sagt mir der Aufwand darüber, wie die Anwendung gebaut ist? Der Satz auf der Folie fasst es zusammen — wenn ein Test fünf Attrappen braucht, ist nicht der Test zu kompliziert, sondern die Klasse zu gut vernetzt.
0:22 Der Test ist dabei ein sehr ehrlicher Zeuge. Er ist das erste Stück Code, das Ihre Architektur wirklich benutzt, und deshalb das erste, das sich beschwert.
Gute Testarchitektur statt Mocking-Überlastung
0:32 Das letzte Modul des Mockito-Tages. Wir erkennen zuerst die typischen Anti-Patterns, schneiden dann die Anwendung so, dass sie ohne Attrappen prüfbar wird, und sprechen zum Schluss über die Pflege des Testcodes selbst. Morgen kommt der Container dazu — und der ersetzt einen Teil dessen, was wir heute mocken.
Mocking-Anti-Patterns erkennen
0:51 Beginnen wir mit dem Symptom, das jeder kennt und das die wenigsten als Symptom lesen: Der Test wird rot, obwohl die Anwendung richtig rechnet. Ein überspezifizierter Test schreibt fest, wie ein Ergebnis zustande kommt, statt welches Ergebnis gilt. Der Unterschied klingt fein und ist im Alltag sehr grob spürbar. Denken Sie an ein Rezept: Sie können prüfen, ob der Kuchen schmeckt, oder Sie können prüfen, ob in Minute drei gerührt wurde.
1:17 Das zweite ist präziser und wertlos, sobald jemand mit einer besseren Küchenmaschine arbeitet. Genau so verhalten sich überspezifizierte Tests. Sie werden rot bei jeder Umstrukturierung, ohne dass sich das Verhalten geändert hat — und der häufigste Weg dorthin führt über Mocks, die mehr prüfen als nötig. Diese vier Felder sind eine Checkliste für den nächsten Code-Review.
1:39 Fünf Mocks im Aufbau: Die Klasse kennt zu viele Nachbarn — das ist eine Aussage über die Anwendung, nicht über den Test. Ein Mock für ein Wertobjekt: Hier hätte ein neues Objekt gereicht, und zwar schneller. Verifikation auf jedem Aufruf: Der Test protokolliert die Implementierung. Und das vierte ist das eindeutigste Zeichen — ein roter Test ohne Verhaltensänderung. Wenn Sie das sehen, hält der Test Struktur fest und nicht Fachlichkeit.
2:06 Jedes dieser Zeichen ist harmlos für sich und ernst zu nehmen, wenn zwei zusammenkommen. Diese Regel klingt nach Dogma und hat sehr praktische Gründe. Ein Betrag, ein Datum, eine Adresse haben kein Verhalten, das zu ersetzen wäre — sie halten Werte. Ein echtes Objekt ist schneller erzeugt als ein Mock konfiguriert, das gilt buchstäblich.
2:26 Und ein gemocktes Wertobjekt liefert null, was den Fehler in die nächste Zeile verschiebt und die Fehlersuche verlängert. Der letzte Punkt ist der interessanteste Befund: Wer den Drang verspürt, ein Wertobjekt zu mocken, hat meistens eine Klasse vor sich, die gar kein Wertobjekt mehr ist — weil sich Verhalten hineingeschlichen hat.
2:45 Diese Tabelle ist das Herzstück des Moduls. Links steht, was Sie im Test sehen — rechts, was in der Anwendung fehlt. Ein Aufbau, der länger ist als der Test: Es fehlt ein Schnitt, der Verantwortung trennt. Ein Mock, der einen Mock liefert, der einen Mock liefert: Es fehlt eine Schnittstelle statt einer Kette. Reihenfolge muss geprüft werden: Es fehlt ein Ergebnis, das die Regel sichtbar macht. Und zwanzig rote Tests nach einem Umbau: Es fehlen Tests am Verhalten.
3:14 Die Fußzeile sagt, warum das funktioniert — der Test ist das erste Werkzeug, das die Architektur benutzt. Der erste Punkt ist die falsche Reparatur: Die Testklasse wird umgebaut, das Problem der Produktionsklasse bleibt. Der zweite ist die Tarnung — eine Basisklasse sammelt den Aufbau ein, und plötzlich sieht kein Test mehr aus, als hätte er fünf Mocks. Die Kopplung ist trotzdem da.
3:37 Der dritte ist verwandt: Tief verschachtelte Mocks machen die Kette kurz und die Abhängigkeit unsichtbar. Und der vierte ist eine Gewöhnung, die viel kostet — rote Tests nach Refactorings gelten als normal. Sie sind ein Befund, und wer sie als normal ansieht, verzichtet auf die beste Rückmeldung, die er bekommt.
Anwendungen testbar schneiden
3:56 Jetzt zur Lösung. Und die liegt nicht im Test, sondern eine Schicht darunter — in der Frage, wer wen überhaupt kennen darf. Ports und Adapter — auch hexagonale Architektur genannt — trennen die Fachlogik von allem, was außen liegt. Stellen Sie sich eine Steckdosenleiste vor: Die Geräte kennen nur den Stecker, nicht das Kraftwerk.
4:17 Die Domäne definiert schmale Schnittstellen, die Ports, und beschreibt damit, was sie braucht. Die Adapter setzen das um — Datenbank, HTTP, Uhr, Dateisystem. Im Test bekommt jeder Port einen Fake, und die Fachlogik läuft ohne jede Infrastruktur. Wichtig ist die Richtung: Der Port wird von der Domäne definiert, nicht von der Datenbank.
4:38 Sonst wandert die Technik einfach eine Schicht nach innen. Lesen Sie diese Schichtung von unten nach oben, denn unten steht das Wertvollste. Die Domäne kennt niemanden — keine Datenbank, kein Framework, keine Uhr. Deshalb ist sie in Millisekunden prüfbar. Darüber der Anwendungsfall, der einen fachlichen Ablauf orchestriert. Dann der Port, die schmale Schnittstelle.
5:01 Und ganz oben die Adapter mit der Technik. Der Pfeil zeigt dabei immer nach innen: Außen kennt innen, nie umgekehrt. Wenn Ihre Domäne eine Datenbankklasse importiert, ist diese Regel verletzt — und dann brauchen Sie für jede fachliche Prüfung eine Attrappe. Das ist die praktische Empfehlung dieses Moduls. Ein Fake wird einmal geschrieben und in dreißig Tests benutzt — die Rechnung geht schnell auf.
5:26 Er hält sich an denselben Vertrag wie der echte Adapter, und man kann beide gegen dieselbe Testsuite laufen lassen; das ist der Punkt, der Fakes von Bastelei unterscheidet. Kein Test muss mehr wissen, in welcher Reihenfolge der Port gerufen wird. Und die ganze Konfiguration verschwindet aus den Testklassen und lebt an einer Stelle.
5:45 Weniger Zeilen, weniger Kopplung, bessere Fehlermeldungen — das ist selten so eindeutig. Zeit, Zufall und Identität — diese drei machen Tests unzuverlässig, und zwar sporadisch, was am unangenehmsten ist. Die Lösung ist in allen drei Fällen dieselbe und steht in der rechten Spalte: Sie kommen als Abhängigkeit über den Konstruktor herein.
6:05 Eine feste Uhr, eine feste Folge, ein mitzählender Generator. Danach braucht kein Test dafür einen statischen Mock — und das Notfallwerkzeug aus dem letzten Modul bleibt im Schrank. Erfahrungsgemäß wird die Zeit als Erstes gezähmt, weil sie am sichtbarsten stört, und der Zufall bleibt liegen. Nehmen Sie beide zusammen mit.
6:25 Der erste Punkt ist der häufigste Fehler beim Einführen von Ports: Der Port wird nach der Datenbank geschnitten statt nach der Fachlichkeit — dann heißt die Methode speichereZeile statt merkeBestellungVor. Der zweite betrifft Fakes: Sie wachsen zur zweiten Umsetzung, die niemand gegen die echte prüft, und irgendwann weichen beide ab.
6:44 Der dritte ist die halbe Arbeit — die Uhr wird hineingereicht, der Zufall bleibt statisch. Und der vierte ist eine schleichende Verlagerung: Der Anwendungsfall zieht Fachlogik an sich, und die Domäne bleibt eine leere Datenhülle.
Testcode pflegen
6:57 Zum Schluss ein Thema, das in vielen Teams gar nicht besprochen wird: Testcode ist Produktionscode mit einem anderen Zweck — und braucht dieselbe Pflege. Ein Test Data Builder erzeugt ein gültiges Objekt mit sinnvollen Voreinstellungen und lässt den Test nur das ändern, worum es geht. Denken Sie an eine Bestellvorlage, in der schon alles ausgefüllt ist und Sie nur das Abweichende eintragen.
7:20 Eine Object Mother ist die andere Variante: benannte Beispiele aus der Domäne — die Stammkundin, die Erstbestellung, der Grenzfall. Beide machen dasselbe sichtbar: was in diesem Test wirklich zählt. Und sie verstecken den Rest, ohne ihn wegzulassen. Das ist der Unterschied zu einem Aufbau, in dem alles gleich wichtig aussieht.
7:39 Lesen Sie diesen Block wie einen Satz: eine Bestellung mit zehn Apfelkisten für einen Stammkunden. Alles andere — Adresse, Lieferfenster, Zahlungsart — ist für diese Regel egal und steht deshalb nicht da. Genau diese Auslassung ist die Aussage. Wenn ein Leser in einem halben Jahr sehen will, wovon der Staffelpreis abhängt, findet er hier drei Angaben statt fünfzehn.
8:01 Der Preis dafür ist eine kleine Klasse, die es zu pflegen gilt. Und die Faustregel dazu kennen Sie schon aus dem dritten Modul: Ab der dritten Wiederholung lohnt sie sich. Der rote Faden ist: absichern, bevor man anfasst. Deshalb steht der kennzeichnende Test an erster Stelle — ein Test, der festhält, was heute passiert. Nicht was passieren sollte.
8:22 Danach suchen Sie die Abhängigkeit, die den Test langsam oder unsicher macht, und reichen sie über den Konstruktor hinein, ohne die Logik anzufassen. Erst dann kommt der Fake, und erst danach die eigentliche Umgestaltung. Der Hinweis in der Fußzeile ist wichtig und wird gerne missverstanden: In Schritt eins gehören auch die Fehler hinein. Wer sie unterwegs korrigiert, verliert das Netz.
8:46 Der erste Punkt ist der Builder, der zur Wartungslast wird — eine Methode je Feld, und irgendwann ist er größer als die Klasse, die er baut. Der zweite betrifft Object Mothers: Sie sammeln Beispiele, bis niemand mehr weiß, welches Beispiel für welchen Testfall gedacht war. Der dritte ist der gefährlichste im Umgang mit Altcode — der kennzeichnende Test wird korrigiert statt festgehalten, und dann sichert er nichts ab.
9:09 Und der vierte ist eine Frage der Kultur: Testcode wird von Reviews ausgenommen, weil er nicht ausgeliefert wird. Ausgeliefert wird er nicht, gepflegt werden muss er trotzdem.
Übung
9:20 Und jetzt räumen wir auf. Der Bestellservice von Krautgut mockt derzeit fünf Abhängigkeiten — und wir schauen, wie viele davon wirklich nötig sind. Der heutige Test von Krautgut mockt Lager, Katalog, Preisrechner, Zahlwerk und Uhr. Ihre Aufgabe ist, mit höchstens einem Test Double auszukommen. Das Erfolgskriterium hat drei Teile, und der mittlere ist der überraschende: Die Testklasse soll kürzer sein als vorher.
9:46 Weniger Zeilen bei gleicher Aussage — das ist die Probe darauf, ob der Umbau wirklich etwas gebracht hat. Und der dritte Teil greift das siebte Modul auf: kein Test verifiziert mehr einen lesenden Aufruf. Diese Übung verändert übrigens ausdrücklich auch Produktionscode. Schritt eins ist die Sortierung: Welche echte Abhängigkeit steckt hinter jedem Mock?
10:08 Schritt zwei ist die Erkenntnis, dass zwei davon gar keine sind — Katalog und Preisrechner rechnen nur, die dürfen echt bleiben. Schritt drei ist die eigentliche Architekturarbeit: ein Port für den Zahlungszugang und ein Fake dafür. Schritt vier leert den Aufbau mit einem Builder. Und Schritt fünf räumt die Verifikationen auf. Die Fußzeile nennt die Messgröße: Zeilen der Testklasse vorher und nachher.
10:32 Schreiben Sie beide Zahlen auf, bevor Sie anfangen — hinterher schätzt man falsch. Der erste Punkt ist die halbe Migration: Der Fake wird gebaut, aber der alte Mock bleibt daneben stehen. Dann haben Sie beides und nichts gewonnen. Der zweite ist der falsch geschnittene Port — nach der Zahlwerk-API statt nach dem, was Krautgut braucht.
10:52 Das merkt man erst, wenn Zahlwerk sich ändert und die Domäne mitziehen muss. Der dritte ist der Builder, der selbst zum unübersichtlichen Aufbau wird. Und der vierte ist der, der die Übung entwertet: Sie endet beim Test, und keine Zeile Produktionscode bewegt sich. Dann bleibt die Ursache stehen.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →