1. Ein Controller – eine Testklasse
Wenn man @WebMvcTest als „Mikroskop“ betrachtet, dann ist der Controller das Objekt, das wir auf den Objekttisch legen. Man kann natürlich versuchen, dort drei Objekte, ein paar Muttern und eine Katze hineinzustopfen (die Katze findet immer einen Weg ins Bild), aber dann verliert man den Überblick, was genau man betrachtet. Die Regel „Ein Controller – eine Testklasse“ ist der Versuch, den Fokus zu bewahren: Eine Testklasse schützt einen HTTP-Vertrag und einen Verantwortungsbereich.
In der Praxis löst diese Regel gleich zwei alltägliche Probleme. Erstens reduziert sie drastisch die Anzahl der „zufälligen Abhängigkeiten“: Wenn Sie einen öffentlichen Controller testen, benötigen Sie keine Mocks und kein Wiring von Editor-/Admin-Controllern. Zweitens macht sie Testfehler diagnostizierbar. Wenn PublicArticleControllerWebMvcTest fehlschlägt, suchen Sie fast immer im öffentlichen Web-Layer nach dem Problem und nicht in „einem der 27 Endpunkte, die wir aus irgendeinem Grund in einer Klasse getestet haben“.
Lassen Sie uns schnell festhalten, was wir in unserem Projekt ContentHub unter „einem Controller“ verstehen. Wir haben verschiedene API-Oberflächen: öffentlich (anonymes Lesen veröffentlichter Artikel), Editor (Erstellen/Bearbeiten/zum Review senden) und Admin (Moderation, Veröffentlichung, Archivierung). Diese Oberflächen können mit demselben Domänenvokabular („Article“) arbeiten, aber es sind dennoch unterschiedliche Verträge. Aus diesem Grund sollten die Tests ebenfalls getrennt leben.
Hier ist das richtige „Skelett“ einer Testklasse für einen Controller:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Wichtig: explizit den Controller angeben, der unter das „Mikroskop“ gelegt wird
@WebMvcTest(PublicArticleController.class)
class PublicArticleControllerWebMvcTest {
// Hier werden nur Abhängigkeiten und Szenarien des öffentlichen Vertrags stehen
}
Und hier ist eine formal funktionierende, aber methodisch gefährliche Variante:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Gefährlich: Ohne Parameter hebt Spring Boot alle gefundenen Controller hoch
@WebMvcTest
class ArticleApiWebMvcTest {
// Der Kontext wächst und der Test wird plötzlich zu „allem Möglichen“
}
Im zweiten Fall sagen Sie Spring Boot: „Heb mir den Web-Slice hoch, aber welche Controller – rate selbst.“ Spring Boot errät es: Es hebt alle gefundenen Controller in diesem Slice hoch. Und dann beginnt die typische Ereigniskette: Sie haben zwar nur eine Testklasse, aber die Anzahl der Abhängigkeiten und das „Warum startet er überhaupt nicht?“ erinnern daran, als hätten Sie die halbe Anwendung hochfahren wollen.
2. Risiken eines universellen @WebMvcTest
Es gibt einen sehr menschlichen Wunsch: „Ich mache eine große Testklasse, um nicht viele Dateien zu erzeugen.“ Dieser Wunsch ist nachvollziehbar und spart sogar ein paar Sekunden beim Erstellen der Klasse. Aber in Tests verwandelt sich eine solche „Ersparnis“ oft in einen Kredit mit 300% Zinsen: Die Zinsen zahlen Sie jede Woche, wenn sich etwas im Code ändert. Ein universeller @WebMvcTest verwischt fast immer die Grenzen des Slices und macht aus einem engen Web-Test eine seltsame Mischung aus „wir testen alles und nichts“.
Der häufigste Effekt sieht so aus: Der Kontext startet nicht, weil einer der Controller im Slice eine Abhängigkeit mit sich bringt, die es in @WebMvcTest standardmäßig nicht gibt. Und plötzlich reparieren Sie nicht den Test der öffentlichen API, sondern das Wiring für einen Admin-Endpunkt – nur weil beide im selben Kontext gelandet sind.
Stellen wir uns ein vereinfachtes Bild vor. Wenn Sie einen „universellen“ @WebMvcTest ausführen, tun Sie im Grunde Folgendes:
flowchart TD Test["ArticleApiWebMvcTest (@WebMvcTest ohne Liste)"] --> C1["PublicArticleController"] Test --> C2["EditorArticleController"] Test --> C3["AdminArticleController"] C1 --> S1["PublicArticleService (braucht Mock)"] C2 --> S2["EditorArticleService (braucht Mock)"] C3 --> S3["AdminArticleService (braucht Mock)"]
Sie könnten sagen: „Na und? Ich mocke einfach alles.“ Ja, das ist möglich. Aber dann geht es in der Testklasse nicht mehr um einen Vertrag, sondern um das Zusammenstellen einer Mock-Sammlung. Und wenn Sie versehentlich einen neuen Controller oder ein neues Argument im Controller-Konstruktor hinzufügen – dieser „universelle“ Test fängt an zu scheitern, selbst wenn Sie noch keine einzige Testmethode für den neuen Endpunkt geschrieben haben. Am Ende verhält sich die Testklasse wie eine Alarmanlage im Treppenhaus: Sie schreit nicht nur bei Feuer, sondern auch, wenn jemand ein Fenster öffnet.
Fast immer führt dies zu folgenden Konsequenzen, und es ist wichtig, das zu fühlen, nicht nur als Slogan zu merken. Erstens sammeln sich in einer Testklasse zu viele @MockitoBean an. Selbst wenn jeder Mock „harmlos“ ist, wird das Gesamtbild zu Lärm. Zweitens werden Tests logisch verknüpft: Sie ändern den Admin-Controller, und plötzlich fallen Public-Tests, weil der Kontext jetzt einen weiteren Bean benötigt. Drittens verlieren Sie unmerklich den Sinn des Slices: Er wird breiter als für die spezifische Prüfung nötig.
Hier eine kurze Veranschaulichung, wie ein „universeller“ Test Sie dazu zwingt, Mocks ohne Grund einzuziehen:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
@WebMvcTest
class ArticleApiWebMvcTest {
// Und das ist erst der Anfang...
// public, editor, admin – alles in einer Box
// Je mehr Controller in den Slice geraten, desto mehr Mocks werden allein zum Kontextstart benötigt
}
Und jetzt vergleichen Sie mit dem fokussierten Ansatz:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Gute Praxis: Teste einen Vertrag und einen Controller
@WebMvcTest(AdminArticleController.class)
class AdminArticleControllerWebMvcTest {
// Hier denken Sie nur über den Admin-Vertrag nach,
// nicht über die gesamte API des Dienstes.
}
Der Unterschied liegt nicht in der Anzahl der Codezeilen. Der Unterschied liegt darin, worüber Sie nachdenken müssen, wenn Sie diese Datei öffnen.
3. API-Oberflächen: public / editor / admin
In ContentHub gibt es eine einfache, aber sehr lebensnahe Tatsache: Dieselbe Ressource „Artikel“ sieht in verschiedenen Zugriffsbereichen unterschiedlich aus. Die öffentliche API – das Lesen von Veröffentlichtem. Die Editor-API – das Erstellen und Bearbeiten des „Eigenen“. Die Admin-API – das Treffen von Entscheidungen und das Verwalten von Status. Selbst wenn die URLs ähnlich sind, sind die Bedeutungen unterschiedlich: verschiedene DTOs, verschiedene Fehlerstatus, verschiedene Erwartungen des Clients. Daher ist es besser, Tests nicht „nach Entität Article“ zu organisieren, sondern nach den tatsächlichen Web-Oberflächen.
Die Regel „Ein Controller – eine Testklasse“ passt perfekt zu diesem Modell. Sie erhalten drei wichtige Testdateien, und jede wird zu einer kleinen Dokumentation ihrer API-Oberfläche. Sie sehen den Namen und wissen sofort, wo Sie sind und warum. Das ähnelt einer guten Ordnerstruktur auf dem Computer: Wenn es Fotos/2025/Urlaub gibt, speichern Sie dort nicht „Steuer“ und „Borschtsch-Rezept“. (Obwohl Borschtsch ein wichtiger Teil des Überlebens ist, da widerspreche ich nicht.)
Praktisch sieht das so aus wie separate Testklassen:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Public: nur öffentliche Endpunkte
@WebMvcTest(PublicArticleController.class)
class PublicArticleControllerWebMvcTest {
}
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Editor: nur Editor-Endpunkte
@WebMvcTest(EditorArticleController.class)
class EditorArticleControllerWebMvcTest {
}
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Admin: nur Admin-Endpunkte
@WebMvcTest(AdminArticleController.class)
class AdminArticleControllerWebMvcTest {
}
Hier gibt es eine wichtige Feinheit: Wir sprechen hier konkret über die Struktur der Test-Suite, nicht über die Mechanik der Anfragen. Daher versuchen wir in dieser Vorlesung nicht, „mit einem Test alles zu beweisen“. Wir bauen ein Gerüst, in dem jeder Vertrag seinen Platz hat und dieser Platz nicht mit den Nachbarn kollidiert.
Für eine etwas anschaulichere Darstellung kann man die Test-Sammlung nach Controllern so darstellen:
flowchart LR P["PublicArticleControllerWebMvcTest"] --> PC["PublicArticleController"] E["EditorArticleControllerWebMvcTest"] --> EC["EditorArticleController"] A["AdminArticleControllerWebMvcTest"] --> AC["AdminArticleController"]
Dieses Schema scheint offensichtlich … bis Sie ein echtes Projekt sehen, in dem es eine ArticleApiTest mit 1500 Zeilen gibt, die „scheinbar alles prüft“, aber bei einem Fehler brauchen Sie eine halbe Stunde, um herauszufinden, welcher Endpunkt überhaupt gefallen ist und warum das mit einem Test zusammenhängt, den Sie gar nicht angefasst haben.
4. Namen und Pakete des WebMvcTest
Wenn die Tests in die Dutzende gehen, beginnt „wo sie liegen“ die Entwicklungsgeschwindigkeit mindestens so stark zu beeinflussen wie die Auswahl der Bibliotheken. Das ist kein Scherz: Wenn die Teststruktur umständlich ist, führen Sie Tests seltener aus und prüfen häufiger „mit den Augen“. Und die Augen sind ein gutes Werkzeug, aber abends neigen sie zu Halluzinationen. Deshalb gehen wir bei Web-Slice-Tests genauso diszipliniert mit Namen und Dateipfaden um wie im Produktionscode.
Im Rahmen von ContentHub halten wir ein einfaches und vorhersagbares Modell: Das Testpaket spiegelt das Produktionspaket wider, und der Testname zeigt klar was getestet wird und mit welcher Testart. Das hilft sehr, @WebMvcTest nicht mit zukünftigen breiteren Tests zu verwechseln, und erleichtert gleichzeitig die Suche. Sie sollten nicht in einer Quest „finde den Test anhand des Namens“ spielen müssen, wenn der Abgabetermin drängt.
Beispiel einer Verzeichnisstruktur (ohne Fanatismus, einfach „damit man es findet“):
src
├─ main/java/com/example/contenthub/api/controller
│ ├─ PublicArticleController.java
│ ├─ EditorArticleController.java
│ └─ AdminArticleController.java
└─ test/java/com/example/contenthub/api/controller
├─ PublicArticleControllerWebMvcTest.java
├─ EditorArticleControllerWebMvcTest.java
└─ AdminArticleControllerWebMvcTest.java
Der Name PublicArticleControllerWebMvcTest erfüllt hier zwei Zwecke. Er sagt „ich teste PublicArticleController“ und präzisiert sofort „ich tue dies als WebMvc-Slice“. Das spart erstaunlich viel Zeit beim Lesen. Besonders wenn Sie nach einem Monat zum Projekt zurückkehren und versuchen zu verstehen, warum ein Test fehlschlägt: Sie raten nicht, welcher Kontext hochgefahren wird und welchen Umfang dieser Test hat.
Um die Idee zu festigen, vergleichen Sie zwei Namen:
PublicArticleControllerWebMvcTest // sofort klar: Controller + Slice
ArticleControllerTest // unklar: welcher Controller? welche Ebene?
Wenn im Projekt aufwändigere Tests auftauchen (vollständiger Kontext, Live-Server usw.), dann beginnen „verschwommene“ Namen wirklich zu stören. Aber selbst ohne zukünftige Ebenen ist jetzt schon eines wichtig: Tests sollten wie Dokumentation lesbar sein, und Dokumentation sollte nicht rätselhaft sein.
Übrigens ist dies ein hervorragender Ort, um das anzuwenden, was Sie bereits über JUnit wissen: @Nested und @DisplayName. Innerhalb einer Controller-Testklasse können Sie Tests nach Endpunkten gruppieren. Wichtig ist nur, dass dies nicht zu „einem zweiten Controller innerhalb derselben Klasse“ wird. Gruppierung nach Endpunkten eines Controllers – ausgezeichnet. Gruppierung „sowohl public, als auch editor, als auch admin“ – bereits ein Alarmzeichen.
5. Gemeinsamer Code ohne Chaos
Sobald Sie die Tests nach Controllern getrennt haben, taucht sofort die nächste Versuchung auf: „Lass uns alles Gemeinsame in eine Basisklasse auslagern, um @Autowired MockMvc, ObjectMapper, URL-Konstanten usw. nicht zu wiederholen.“ Diese Versuchung ist besonders stark bei Anfängern, weil Wiederholung „schlecht“ und Abstraktion „schön“ erscheint. In Tests ist alles etwas kniffliger: Wiederholung ist manchmal wirklich schlecht, aber eine verfrühte Abstraktion ist fast immer schlimmer. Denn sie versteckt den Sinn des Szenarios und verwischt die Grenzen.
Die praktische Regel hier ist: Gemeinsamer Code ist zulässig, wenn er die Bedeutung des Tests nicht verändert und Sie nicht zwingt, über „Magie“ nachzudenken. Zum Beispiel ist eine gemeinsame Konstante für die Basis-URL oder eine kleine Methode, die ein DTO-Objekt in einen JSON-String umwandelt, normalerweise in Ordnung. Aber eine gemeinsame Basisklasse, die in @BeforeEach eine Menge Stubbing für drei verschiedene Controller macht, verwandelt sich fast garantiert in eine Falle: Tests beginnen von nicht offensichtlichem „vererbtem“ Verhalten abzuhängen, und Sie fangen Fehler ein, wo Sie sie nicht erwartet haben.
Bei @WebMvcTest ist es besonders wichtig, dass jede Testklasse ihre Abhängigkeitsgrenzen klar zeigt. Daher ist es sinnvoll, Mocks lokal zu deklarieren – in der Nähe der Tests, die sie verwenden. Es geht nicht um „Liebe zur Kopierpaste“, sondern um die Ehrlichkeit des Kontexts. Wenn Sie PublicArticleControllerWebMvcTest öffnen, möchten Sie sehen, welcher Service ersetzt wird und warum. Wenn dies in AbstractWebMvcTestBase versteckt ist, verwandeln Sie den Testcode in einen Detektivfall.
Ein guter Kompromiss sieht oft so aus: Ein kleiner Helfer ohne Spring-Annotationen, der in src/test/java/.../testsupport lebt und 1–2 einfache Hilfsfunktionen enthält. Zum Beispiel die Generierung eines Test-DTOs oder ein minimaler JSON-Loader (wenn Sie Fixtures verwenden). Aber selbst hier ist es sinnvoll, Disziplin zu wahren: Der Helfer sollte das Lesen des Tests unterstützen, nicht in ein „Mini-Framework“ führen.
Ich zeige ein Beispiel eines zulässigen Mini-Helfers, der die Grenze nicht verletzt und keinen Kontext mitbringt:
import com.fasterxml.jackson.databind.ObjectMapper;
final class JsonTestHelper {
private JsonTestHelper() {
// Hilfsklasse: keine Instanzen nötig
}
static String toJson(ObjectMapper mapper, Object value) throws Exception {
// Wichtig: Die Serialisierung muss dieselbe sein wie in der Anwendung (derselbe ObjectMapper aus dem Kontext)
return mapper.writeValueAsString(value);
}
}
Und ein Beispiel für das, was normalerweise zu schaden beginnt (nicht weil „es verboten wäre“, sondern weil es schnell unkontrollierbar wird):
abstract class AbstractArticleApiWebMvcTestBase {
// Gefährlich: In einer solchen Basisklasse tauchen schnell Mocks für public/editor/admin auf,
// gemeinsames Setup, und nach einer Woche erinnert sich niemand mehr, was und warum.
}
Eine weitere Feinheit: Manchmal wiederholen sich URL-Teile bei Controllern. Zum Beispiel haben public-Endpunkte /api/public/articles, editor-Endpunkte /api/editor/articles. Ja, man kann die Zeichenfolgen in Konstanten auslagern. Aber wenn der Test dadurch wie eine mathematische Formel aussieht, die man ausrechnen muss, um die URL zu verstehen, ist es besser, die Zeichenfolge direkt im Test zu lassen. Ein Controller-Test ist ein Vertragstest. Es ist hilfreich, den Vertrag mit eigenen Augen zu sehen, nicht „aus Stücken zusammensetzen“ zu müssen.
6. Fallstudie: Zerschneiden eines „dicken“ WebMvcTest
Die Regel „Ein Controller – eine Testklasse“ wird am besten im Kontrast sichtbar: wenn Sie einen „dicken“ Test nehmen und ihn in drei schlanke verwandeln. Stellen wir uns vor, wir haben mit der Idee begonnen: „Ich prüfe alle Artikel in einem Test.“ Herausgekommen ist eine Klasse, in der sowohl public list, als auch editor create, als auch admin approve gelandet sind. Und irgendwann erforderte sie drei verschiedene Services und drei verschiedene Stubbing-Sets – nur um den Kontext zu starten.
So könnte ein typisches „dickes“ Skelett aussehen (selbst ohne Testmethoden riecht es bereits nach Überhitzung):
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Absichtlich „dicker“ Slice: drei Controller in einem Kontext
@WebMvcTest({
PublicArticleController.class,
EditorArticleController.class,
AdminArticleController.class
})
class AllArticleControllersWebMvcTest {
// Je mehr Controller hier, desto mehr Mocks werden unten benötigt
}
Damit er startet, müssen Sie die Abhängigkeiten aller drei Controller ersetzen. Vereinfacht ausgedrückt wird das schnell so:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
// Hier sind mindestens public + editor in den Kontext gelangt, und ihre Abhängigkeiten müssen gemockt werden
@WebMvcTest({PublicArticleController.class, EditorArticleController.class})
class AllArticleControllersWebMvcTest {
@MockitoBean
PublicArticleService publicArticleService; // Mock für Abhängigkeiten des Public-Controllers
@MockitoBean
EditorArticleService editorArticleService; // Mock für Abhängigkeiten des Editor-Controllers
}
Und dann ist die „Wachstumslogik“ fast unvermeidlich: Admin hinzugefügt – AdminArticleService hinzugefügt. Neue Abhängigkeit im Editor-Controller hinzugefügt – noch ein Mock hinzugefügt. Noch ein Controller hinzugefügt – noch ein Mock. Und so weiter.
Jetzt der gleiche Sinn, aber in drei separaten Klassen. Public:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Enger Kontext: nur public-Schicht
@WebMvcTest(PublicArticleController.class)
class PublicArticleControllerWebMvcTest {
}
Editor:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Enger Kontext: nur editor-Schicht
@WebMvcTest(EditorArticleController.class)
class EditorArticleControllerWebMvcTest {
}
Admin:
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
// Enger Kontext: nur admin-Schicht
@WebMvcTest(AdminArticleController.class)
class AdminArticleControllerWebMvcTest {
}
Das Ergebnis: Jede Testklasse hebt nur das hoch, was sie benötigt, und erfordert nur die Mocks, die tatsächlich an der Grenze dieses Controllers stehen.
Um zu spüren, wie sehr das das Leben erleichtert, stellen Sie sich eine typische Arbeitssituation vor. Sie ändern einen Editor-Endpunkt (z. B. ändert sich das Request-DTO). Wenn Sie einen „dicken“ Test haben, besteht die Gefahr, dass er bereits vor dem Start der Public-Tests fehlschlägt, weil der Kontext nicht aufgebaut wird. Wenn die Editor-Tests separat sind, arbeiten die Public-Tests normal weiter und zeigen: Der öffentliche Vertrag ist nicht betroffen. Das ist genau die Lokalität, wegen der wir überhaupt Slice-Tests wählen.
Abschließend eine kleine „ingenieurliche Ehrlichkeit“ zur Geschwindigkeit. Ja, drei verschiedene @WebMvcTest-Klassen erzeugen drei verschiedene Kontexte. Das kann etwas teurer sein als ein „universeller“ Kontext. Aber normalerweise gewinnt etwas anderes: Die Kontexte sind kleiner, die Tests einfacher, die Diagnose schneller, und Sie verbringen weniger Zeit mit dem Kampf gegen „überflüssige“ Abhängigkeiten. Und wenn Sie jemals den Wunsch verspüren, die Geschwindigkeit zu optimieren, sollten Sie das mit Messungen tun, nicht durch Raten. Der teuerste Test ist nicht der, der 700 ms statt 400 ms läuft, sondern der, der einen Entwickler zwei Stunden lang zwingt, das Problem an der falschen Stelle zu suchen.
Zur besseren Orientierung können Sie eine kleine Tabelle der „Ausbreitungssymptome“ im Kopf behalten. Nicht als Dogma, sondern als Signal „Stopp und überprüfe die Grenze“:
| Symptom im Controller-Test | Was es oft bedeutet | Wie es normalerweise behoben wird |
|---|---|---|
| 4–6 @MockitoBean in der Testklasse | Es sind zusätzliche Controller in den Kontext geraten, oder der Controller macht zu viel | @WebMvcTest auf einen Controller eingrenzen, Architektur des Controllers prüfen |
| @WebMvcTest ohne Parameter | Alle Controller des Projekts wurden eingeschaltet | Konkreten Controller in der Annotation angeben |
| Tests schlagen wegen Abhängigkeiten eines Endpunkts fehl, den Sie nicht testen | Der Testkontext wurde „für alle Fälle“ aufgebaut | Tests nach Controllern trennen |
| Im Projekt taucht eine „gemeinsame Basis-Testklasse“ mit Magie auf | Sie beginnen, Ihr eigenes Test-Framework zu bauen | Vereinfachen, Mocks und Setup näher an die Tests bringen |
7. Typische Fehler bei @WebMvcTest
Fehler Nr. 1: @WebMvcTest ohne Angabe des Controllers verwenden.
Technisch sieht das hübsch aus: eine Annotation, minimale Parameter. Praktisch bedeutet es, dass alle Controller in den Slice aufgenommen werden. Dann testen Sie nicht „einen Vertrag“, sondern bauen ein „Mini-Bild der gesamten API“, und jede Änderung in einem beliebigen Controller kann Ihre Testklasse kaputt machen, selbst wenn es noch keine Tests für diesen Endpunkt gibt.
Fehler Nr. 2: Versuchen, „einen Test für alle Artikel“ zu machen, weil „das weniger Dateien sind“.
Bei Web-Layer-Tests ist die Dateigröße fast nie der Hauptparameter für Qualität. Der Hauptparameter ist die Lokalität der Verantwortung. Wenn Sie public/editor/admin in einer Klasse vermischen, zahlen Sie mit Komplexität: mehr Mocks, mehr zufällige Abhängigkeiten, mehr Fehlerursachen. Am Ende gibt es weniger Dateien, aber mehr Schmerz.
Fehler Nr. 3: Ein riesiges gemeinsames Setup bauen und es in der Vererbung verstecken.
Vererbung in Tests scheint bequem, bis Sie versuchen zu verstehen, warum ein Service in einem bestimmten Test „Standard“-Daten zurückgibt. Wenn Stubbing und Vorbereitung in der Basisklasse versteckt sind, hört der Test auf, ein Szenariodokument zu sein, und wird zu einer Geschichte mit fehlenden Seiten. Es ist besser, wenn der Test 3 Zeilen mehr hat, aber sichtbar ist, was passiert.
Fehler Nr. 4: URLs und Erwartungen in zu clevere Konstanten und „Mini-DSLs“ auslagern.
Ein Controller-Test dreht sich um den HTTP-Vertrag. Es ist hilfreich, den Vertrag mit eigenen Augen zu sehen: /api/public/articles/{slug} ist kein „schlechter String“, sondern ein wichtiger Teil der Schnittstelle. Wenn Sie den Test in eine Reihe von Aufrufen wie publicApi().articles().details(slug) ohne Notwendigkeit verwandeln, verlieren Sie das Gefühl für das reale HTTP. Und dann wundern Sie sich, warum ein Bug „in der URL“ lag, der Test dies aber nicht gezeigt hat.
Fehler Nr. 5: Den architektonischen Geruch eines „God-Controllers“ nicht bemerken.
Manchmal gibt es viele Tests und man möchte sie trotzdem in einer Klasse „vereinen“ – weil der Controller riesig ist und 15 Endpunkte hat. Das ist oft kein Problem der Tests, sondern ein Signal im Produktionscode: Der Controller hat zu viele Verantwortungen übernommen. Im Rahmen des Kurses schreiben wir die Architektur nicht um, aber wir sollten zumindest das Signal erkennen: Wenn ein Controller schwer eng zu testen ist, ist er möglicherweise auch inhaltlich zu breit.
GO TO FULL VERSION