Sociable Tests
Warum entstehen hunderte Unit-Tests, aber die Software läuft trotzdem nicht? Weil isolierte Tests allein kein Feature beweisen – und das ist das Problem.

Sociable Unit Tests bezeichnen automatisierte Tests, die nicht einzelne Klassen isoliert prüfen, sondern vollständige Use Cases und Features vom UI-Einstieg bis zur Datenbank durchlaufen. Entwickler tragen dafür die Verantwortung. Mocking externer Systeme bleibt die Ausnahme, Datenbankzugriffe gehören zum Test dazu.
Das Wichtigste in Kürze
- Entwickler sind fertig, wenn der Testcode vorliegt, der das Feature nachweist, nicht wenn der Produktivcode kompiliert.
- Sociable Unit Tests, die vollständige Use Cases und Features durchstechen, haben eine unbestreitbare Daseinsberechtigung; isolierte Unit Tests sind bestenfalls ein Nice-to-have.
- Wer die Datenbank wegmockt, testet ein System, das sich anders verhält als in Produktion, und verliert damit die Aussagekraft des gesamten Tests.
- Testdaten-Builder reduzieren das Anlegen eines Geschäftsobjekts auf einen Einzeiler und senken die Hemmschwelle für Entwickler, überhaupt Tests zu schreiben.
- Design for Testability ist eine bewusste Architekturentscheidung: Wer seinen Technologie-Stack gezielt klein hält, kann Use-Case-Tests direkt im Unit-Test-Framework ausführen, ohne Browser-Automatisierung.
Entwickler sind dafür verantwortlich, dass die Software läuft
Die Aufgabe eines Entwicklers endet nicht bei einer Sammlung funktionierender Einzelteile. Sie endet erst, wenn die Features und Use Cases tatsächlich zusammenspielen und das produzieren, wofür der Entwickler bezahlt wird.
Genau hier liegt ein verbreitetes Problem. Selbst Teams, die Testen als notwendig akzeptieren, schreiben oft viele kleine Tests, an deren Ende die Software trotzdem nicht läuft. Die Komponenten funktionieren isoliert, aber ihr Zusammenspiel bleibt ungeprüft.
Jan Leßner bringt diese Verantwortung auf einen klaren Punkt: Entwickler müssen sicherstellen, dass Use Cases und Features so funktionieren, wie sie bestellt wurden. Das automatisiert nachzuweisen ist keine Kür, sondern Teil der eigentlichen Arbeit.
Was sind isolierte und sociable Unit Tests?
Martin Fowler unterscheidet zwei Arten von Unit Tests, für die Entwickler zuständig sind: isolierte und sociable Unit Tests. Beide gehören in die Verantwortung des Entwicklers, doch sie leisten Unterschiedliches.
Isolierte Unit Tests prüfen einen einzelnen Baustein für sich. Sie sind das “nice to have”, auf das sich Entwickler gerne stürzen, weil sie so einfach zu schreiben sind. Ihr blinder Fleck: Sie ignorieren, dass die getesteten Teile kollaborieren müssen, um überhaupt ein Feature herzustellen.
Sociable Unit Tests prüfen größere Einheiten. Die Einheit mit der unbestreitbaren Daseinsberechtigung ist dabei ein Use Case oder ein Feature. Wer sich nur auf isolierte Tests beschränkt, lässt das Wichtigste weg: den Nachweis, dass das Zusammenspiel das gewünschte Verhalten ergibt.
Wer Feature-Tests wegdelegiert, schiebt seine Verantwortung weg
Die Testpyramide hat weiterhin Bestand, solange sie nicht missbraucht wird. Unten das Gros der automatisierten Unit Tests in Entwicklerhand, oben die Abnahmetests der Kunden, dazwischen die Integrationstests.
Die mittlere Schicht ist die Grauzone. Ursprünglich meinte “Integrationstest” jene Tests, bei denen ein System in einen Kontext gestellt wird und mit anderen Subsystemen kommuniziert. Heute wird oft alles dorthin verschoben, wofür ein Entwickler keine Idee hat, wie er einen isolierten Test schreibt.
Der Klassiker: Frontend und Backend werden getrennt entwickelt, und niemand weiß, wie man sie gemeinsam testet. Also macht es “die Testabteilung”. Damit wandern die sociable Unit Tests nach oben, mit dem Etikett “not my job”.
Besonders deutlich wird das bei Microservices. Wer eine Funktionalität ohne Not in viele einzelne Services zerhackt und dann sagt, er müsse nur noch seinen eigenen Service testen, delegiert Verantwortung weg, die früher bei ihm lag. Das ist ein No-Go. Die Verantwortung für das Zusammenspiel bleibt beim Entwickler.
Headless End-to-End-Tests umgehen langsame UI-Werkzeuge
UI-Testwerkzeuge wie Selenium oder Cypress sind für den durchschnittlichen Entwickler schwer zu handhaben. Sie bilden eine eigene Ausdruckswelt, erzwingen viel redundanten Code und sind vor allem langsam. Genau das hält Entwickler davon ab, ganze Features zu testen.
Eine Alternative sind Headless End-to-End-Tests. Dabei wird die reine Repräsentationsschicht oben komplett heruntergestrippt, also alles, was nur der Anzeige dient. Getestet wird alles darunter: von der UI-Logik über die Geschäftslogik bis in die Datenbank, von oben bis unten durch.
Technisch lassen sich diese Tests in das übliche Unit-Test-Framework einbauen, etwa in JUnit. Im beschriebenen Projektkontext hilft GWT, weil der gesamte Client-Code ebenfalls Java ist. Über eine Simulation der Server-Schnittstelle läuft dann alles in einer JVM, ganz wie ein normaler Unit Test.
Diese Möglichkeit hat nicht jedes Team. Aber sie ist kein reiner Glücksfall, sondern Ergebnis einer bewussten Designentscheidung.
Testbarkeit gehört in die Architekturentscheidung
Design for Test bedeutet, die Testbarkeit schon bei der Wahl von Frameworks und Architektur mitzudenken. Wer große Units testbar machen will, bekommt das nicht geschenkt.
Im genannten Projekt fiel die Entscheidung für GWT zunächst aus einem anderen Grund: Das Team wollte keine Trennung zwischen Frontend- und Backend-Entwicklern, sondern Full-Stack-Entwickler. Ein kleiner, vollständig beherrschbarer Stack war das Ziel. Mit Java auf beiden Seiten ergab sich die gute Testbarkeit als Nebeneffekt.
Daraus folgt eine allgemeine Haltung: Nicht einfach das nehmen, was die Spatzen von den Dächern pfeifen. Je stärker du deine Möglichkeiten beschränkst, desto eher kannst du dafür sorgen, dass einzelne Aspekte deiner Architektur wirklich gut funktionieren.
Wer zu allen Seiten offen ist, ist halt auch nicht ganz dicht. Je stärker ich mich beschränken kann, umso eher kann ich mich fokussieren, dass bestimmte Aspekte sehr gut funktionieren.
Jan Leßner
Warum “fertig entwickelt, muss nur noch testen” eine Denkfalle ist
Der Satz “Ich habe fertig entwickelt, ich muss nur noch testen” enthält zwei Denkfallen. Beide verraten, dass jemand noch vor dem entscheidenden Sprung steht.
Die erste Denkfalle ist die Annahme, Tests gäbe es umsonst dazu. Wer den Produktivcode gebaut hat, aber keine Tests, ist nicht fertig. Eine Iteration ist erst abgeschlossen, wenn der Test nachweist, dass etwas geliefert wurde. Die Alternative wäre, dem Kunden zu sagen: “Probier es aus.” Das geht nicht.
Die zweite Denkfalle steckt im Wort “noch”. Wer Tests erst hinterher baut, hat den Produktivcode in langsamen Edit-Compile-Test-Zyklen entwickelt: Anwendung hochfahren, klicken, runterfahren, wieder hochfahren. Diesen Kreislauf gilt es zu unterbrechen.
Werden Tests parallel zum Produktivcode geschrieben, beschleunigen sie bereits dessen Entwicklung. Du bekommst Produktivcode und Testcode in derselben Zeit, die du sonst nur für den Produktivcode gebraucht hättest. Voraussetzung: Tests schreiben muss leichtgewichtig sein.
Tests mit echter Datenbank statt weggemocktem Kern
Der Grundsatz lautet: Was integraler Bestandteil des Systems ist, wird nicht weggemockt. Datenbank, Persistenz, Messaging-Systeme gehören dazu.
Wer die Datenbank wegmockt, testet ein System, das sich mit hoher Wahrscheinlichkeit anders verhält als in Produktion. Nur weil eine Socket-Verbindung dazwischen liegt, ist die Datenbank nicht plötzlich fremd. Deshalb laufen die Tests mit Datenbank. Docker macht es heute einfach, alles Nötige lokal zu installieren.
Damit fällt der wichtigste Grund für Mocking-Frameworks weg, nämlich die Datenbank schnell wegzumocken. Bleibt die Frage, wo Mocking überhaupt noch nötig ist.
Mocks nur für echte externe Systeme
Mocks gehören dorthin, wo Software wirklich externe Systeme anspricht, die in Entwicklung und Abnahmetest gar nicht existieren. Ein Dokumentenmanagementsystem, das es nur beim Kunden gibt, ist so ein Fall.
Davon gibt es meist nur eine Handvoll: ein DMS, ein E-Mail-Server, einige wenige weitere. Bei so wenigen Stellen lohnt es sich, die Mocks mit mehr Sorgfalt zu bauen, statt sie über ein Framework zu streuen.
Ein tragfähiges Pattern steuert diese Mocks über Datenbanktabellen. Im Test werden bestimmte Werte eingestellt, und das externe System verhält sich genau so, wie der Test es braucht. Derselbe Mock lässt sich im Abnahmetest weiterverwenden, wo die echten Systeme ebenfalls fehlen.
Gegen Mocking-Frameworks im Dauereinsatz sprechen zwei Punkte: Mock-Implementierungen sind schwer zu verstehen, vor allem weil selten dabeisteht, warum ein Mock etwas tut. Und sie kosten zusätzlichen Implementierungsaufwand.
Wie machst du Testdaten leichtgewichtig?
Schnelle Testdaten entscheiden darüber, ob Entwickler überhaupt gerne Tests schreiben. Wer für jeden Test ein aufwendiges Kundenkonstrukt zusammenbauen muss, lässt es lieber sein.
Das Mittel der Wahl ist das Builder-Pattern. Ein Geschäftsobjekt in der Datenbank anzulegen sollte ein Einzeiler sein. Im beschriebenen Projekt genügt eine Zeile wie Customer.Builder..., und der Kunde existiert.
Wenn das Anlegen so fluffig ist, kippt die Motivation. Im Idealfall sagt ein Entwickler: Die Anwendung starten ist mir zu mühsam, ich schreibe lieber schnell einen Test. Erreicht ein Team diesen Punkt, hat sich das Verhältnis zum Testen grundlegend gedreht.
Langsame Tests sind kein Schicksal
Wenn ein Test 30 Sekunden zum Hochfahren braucht, kommt kein Entwickler in den Flow. Er checkt E-Mails, holt sich Kaffee und verliert den Faden.
Häufig wird solche Langsamkeit schulterzuckend hingenommen, etwa beim Hochfahren eines Spring-Containers. Spring-gegeben gilt dann wie gottgegeben. Design for Test bedeutet, das nicht zu akzeptieren.
Die JVM selbst braucht ungefähr eine Sekunde. Was passiert in den restlichen 29? Diese Frage lässt sich mit Profiler und Debugger beantworten. Wer dem auf den Grund geht, holt die Wartezeit auf wenige Sekunden herunter, und dann macht Testen Spaß.
Wie steigst du in Design for Test ein, wenn die Software schon da ist?
Bestehende Software, die nie für Testbarkeit ausgelegt war, macht den Einstieg schwerer, aber nicht unmöglich. Ein Projekt mit 300.000 Zeilen Code und nur 20 nichtssagenden Mockito-Tests lässt sich faktisch wie bei null behandeln.
Bohre nicht zuerst das dickste Brett. Schaffe Grundlagen, an denen Entwickler merken, dass plötzlich Dinge möglich werden. Ein guter erster Schritt ist eine kaskadierte Systemkonfiguration: eingecheckte Defaults, die ein Test programmatisch überschreibt. So bekommt jeder Entwickler seine eigene Datenbank, und jede Art von Konfiguration wird testbar.
Aus diesem Bootstrap ergeben sich die nächsten Schritte fast von selbst. Stört die Startzeit, klopfst du sie klein. Danach kommen die Builder. Erst von Hand, dann fällt auf, dass ihre Struktur sich eins zu eins aus den Geschäftsobjekten ergibt.
Statt ein Generator-Framework einzukaufen, reicht oft ein selbstgebauter Generator: per Java Reflection die Geschäftsklassen auslesen, mit System.out den Code rausschreiben, in die Klasse kopieren, fertig. Lauter kleinschrittige Automatisierungen, die schnell zu machen sind.
Ein pragmatischer Einstieg für einen neuen Test: Schreib zuerst die eine Zeile hin, wie du dir den Aufruf wünschst. Dann arbeitest du dich rückwärts dahin, dass es genau so leicht geht.
Ähnliche Beiträge

Richard Seidl
•2. Juni 2026
Patient Agilität: Liegt agiles Arbeiten im Sterben?

Richard Seidl
•26. Mai 2026