1. Die Rolle von RestClientModerationClient in Tests
Wenn in einer Anwendung ein externer HTTP-Aufruf auftaucht, möchte man alles mit einem großen Integrationstest prüfen: @SpringBootTest hochfahren, den Publikationsflow durchlaufen und hoffen, dass es irgendwie klappt. Das Problem: Wenn in einem solchen Test etwas kaputt geht, erhalten Sie eine Million-Fragen: „Ist die Geschäftslogik kaputt? Die Konfiguration? Die Serialisierung? Die Sicherheit? Oder hat sich das externe JSON plötzlich geändert?“. Und dann gehen Sie mit dieser Frage im Büro (oder in Ihrer Küche) herum, wie mit einer Katze, die sauer auf Sie ist, aber Sie wissen nicht warum.
Die Grenze hat bereits zwei Verantwortlichkeiten getrennt: Der Service entscheidet über den Port, und HTTP lebt innerhalb des Adapters. Jetzt müssen wir den Übersetzer selbst testen – RestClientModerationClient. Unten wird dies unsere Arbeits-Baseline für den Outbound-Block sein: dasselbe Request/Response DTO, dieselbe baseUrl und derselbe MockRestServiceServer.
Daher hat der Outbound-Adapter seinen eigenen Verantwortungsbereich und seine eigene separate Prüfung. RestClientModerationClient muss zwei Dinge beweisen: dass er die korrekte HTTP-Anfrage erzeugt (URL, Methode, Header, Body) und dass er die externe Antwort korrekt in ein für unsere Domäne verständliches Ergebnis umwandeln kann. Geschäftsentscheidungen auf der Ebene „bei BLOCK übersetzen wir den Artikel in REJECTED“ leben und werden im Service um den Port herum getestet, aber die Korrektheit von „HTTP → Domänenmodell“ liegt in der Verantwortung des Adapters.
Um den Adapter ohne Netzwerk zu testen, benötigen wir einen speziellen Modus: Wir heben einen minimalen Spring-Kontext nur für den Client an und fangen seine Anfragen mit einem lokalen Stub-Server ab. Genau das macht @RestClientTest zusammen mit MockRestServiceServer.
2. Möglichkeiten von @RestClientTest
Wenn Sie bisher in der Welt „es gibt @SpringBootTest, alles andere ist Kompromiss“ gelebt haben, wirkt @RestClientTest zunächst seltsam. Aber in Wirklichkeit ist es eine sehr ehrliche Ingenieursposition: Wir heben genau so viel Spring hoch, wie nötig ist, um den HTTP-Adapter zu testen, und schleppen nicht die gesamte Anwendung in den Test.
@RestClientTest ist ein Test-Slice für die Client-Seite. Er zielt auf Klassen ab, die extern über HTTP kommunizieren (in unserem Fall RestClientModerationClient). Im Inneren enthält er die Autokonfiguration, die für RestClient, Jackson und Testwerkzeuge rund um HTTP benötigt wird. Und das Wichtigste: Er kann den MockRestServiceServer hochfahren, der die Anfragen abfängt und eine vordefinierte Antwort zurückgibt, ohne auf das reale Netzwerk zuzugreifen.
Um das leichter im Kopf zu behalten, vergleichen wir drei ähnliche Modi:
| Test-Modus | Was wir prüfen | Was wird hochgefahren | Was wird NICHT hochgefahren (und das ist ein Plus) |
|---|---|---|---|
| Unit-Test des Adapters (ohne Spring) | Meist wenig, weil RestClient in der Spring-Welt lebt | Nichts | Keine Autokonfiguration, aber viel manueller Aufwand |
| @RestClientTest | Externer HTTP-Vertrag des Clients + Response-Mapping | Enger Kontext für RestClient + MockRestServiceServer | Wir schleppen kein MVC, keine Security, keine Repositories und Ihr ganzes Universum mit |
| @SpringBootTest | Durchgehendes Szenario (Wiring + Systemverhalten) | Voller Anwendungskontext | Teuer, langsam, schwieriger zu diagnostizieren „was kaputt ist“ |
Wichtig ist, die Einschränkung ehrlich zu benennen: @RestClientTest beweist nicht, dass Ihre Service-Schicht den Port in einem Geschäftsszenario korrekt verwendet. Es beweist, dass ein bestimmter HTTP-Client korrekt mit dem externen Vertrag spricht und die Antwort korrekt in Domänentypen übersetzt. Für ContentHub ist dies eine ideale „mittlere Sicherheitsschicht“: kein reiner Unit-Test, aber auch noch kein schwerer Full-Integration-Test.
3. HTTP-Simulation mit MockRestServiceServer
Das Wort „Server“ im Namen MockRestServiceServer klingt, als würden wir jetzt einen Tomcat hochfahren, einen Port abhören und mit Konflikten kämpfen. Gute Nachricht: Nein. Sehr gute Nachricht. Wirklich „Keks gegessen und gemerkt, dass das Leben wieder Sinn hat“.
MockRestServiceServer fungiert als Abfangjäger für Anfragen, die von RestClient gesendet werden. Es benötigt keine echte Netzwerkverbindung. Sie beschreiben im Voraus eine Erwartung: „Erwarte eine Anfrage an diese URL, mit dieser Methode, mit diesem Body“ und definieren eine Antwort: „Gib dieses JSON zurück“. Dann rufen Sie Ihren Adapter wie gewohnt auf, und er „glaubt“, dass er nach außen gegangen ist.
Das mentale Modell könnte so aussehen:
sequenceDiagram
participant A as RestClientModerationClient
participant R as RestClient
participant M as MockRestServiceServer
A->>R: POST https://moderation.local/api/check
R->>M: "Abfangen der Anfrage innerhalb des Tests"
M-->>R: 200 OK + JSON body
R-->>A: ModerationResponse
Wir testen also den echten Code des Adapters, aber anstelle des realen externen Moderation-Service haben wir einen „Simulator“: Er gibt genau die Antworten zurück, die wir testen möchten. Dadurch können wir den externen Vertrag mit Tests genauso festhalten, wie wir den DTO-JSON-Vertrag mit @JsonTest festgehalten haben, nur dass es jetzt um den Vertrag zwischen Diensten geht (innerhalb einer JVM, ohne Netzwerk).
4. Grundlegende Modelle und der RestClient-Adapter
Bevor wir den Test schreiben, müssen wir über denselben Code sprechen. Unten ist die Arbeits-Baseline des Clients: ein separates externes DTO, RestClient.Builder und baseUrl an einem Ort. Es gibt keine „Magie“ und keine Geschäftsverzweigungen; nur das, was der Adapter braucht: Eingangs-Request, externer Response und die Domänenentscheidung.
Beginnen wir mit dem Port und den Domänentypen:
import java.util.Objects;
// Port (Interface) — Einstiegspunkt für die Integration in die Domäne.
// Der Domäne ist es egal, wie genau der HTTP-Aufruf implementiert ist.
public interface ModerationClient {
ModerationDecision moderate(ModerationRequest request);
}
// Domänen-Request: das, was wir im externen Dienst prüfen möchten.
public record ModerationRequest(String title, String body) {
public ModerationRequest {
// Basisschutz vor "leeren" Aufrufen: Domänendaten dürfen nicht null sein.
Objects.requireNonNull(title);
Objects.requireNonNull(body);
}
}
// Domänenentscheidung: Sie wird von der Geschäftslogik verwendet (nicht vom HTTP-Client).
public record ModerationDecision(ModerationResult result, String reason) {
}
// Domänen-Enum: Genau dieses muss der Adapter nach dem Parsen der Antwort zurückgeben.
public enum ModerationResult {
OK, WARN, BLOCK
}
Als nächstes benötigen wir das externe Payload. Normalerweise ist es praktisch, es als separaten Typ zu halten, um externes JSON nicht mit internen Domänenentscheidungen zu vermischen:
// DTO des externen Dienstes: Die Struktur entspricht dem JSON-Vertrag des Moderation-Service.
public record ModerationResponse(ModerationResult result, String reason) {
// Explizite Umwandlung von "externer Antwort" in "Domänenentscheidung".
ModerationDecision toDecision() {
return new ModerationDecision(result, reason);
}
}
Jetzt der Adapter selbst. In einem echten Projekt werden Sie mehr Nuancen haben (Timeouts, Fehlerbehandlung, Logging), aber für die Vorlesung reicht es, das Wesentliche zu zeigen: Einen POST-Request erstellen, ModerationRequest senden, ModerationResponse lesen und ModerationDecision zurückgeben.
import org.springframework.http.MediaType;
import org.springframework.web.client.RestClient;
public final class RestClientModerationClient implements ModerationClient {
private final RestClient restClient;
public RestClientModerationClient(RestClient.Builder builder, String baseUrl) {
// RestClient wird über den Builder erstellt, damit Spring seine Konfiguration im Test-Slice einspielen kann.
this.restClient = builder.baseUrl(baseUrl).build();
}
@Override
public ModerationDecision moderate(ModerationRequest request) {
// Wichtig: Endpunkt festlegen und dass wir einen JSON-Body senden.
ModerationResponse response = restClient.post()
.uri("/api/check") // Pfad relativ zu baseUrl
.contentType(MediaType.APPLICATION_JSON)
.body(request) // Request-Body: ModerationRequest wird von Jackson serialisiert
.retrieve()
.body(ModerationResponse.class); // Erwarte konkretes externes DTO
// Der Adapter ist verantwortlich für die Übersetzung "HTTP/JSON" -> "Domänentypen".
return response.toDecision();
}
}
Beachten Sie: Hier akzeptiert der Adapter RestClient.Builder (damit Spring ihn im Test-Slice ordentlich konfigurieren kann) und separat baseUrl. In einem vollwertigen ContentHub wäre dies meist ein @ConfigurationProperties, aber jetzt ist es wichtig, das Prinzip zu zeigen, nicht einen Wettbewerb der Annotationen zu veranstalten.
Unsere Aufgabe ist es nun zu prüfen, ob der Adapter bei einer gegebenen externen Antwort "OK" ein ModerationDecision(OK, null) zurückgibt und bei "BLOCK" eine Entscheidung mit Grund. Parallel möchten wir sicherstellen, dass der Request an die richtige URL und mit der richtigen Methode gesendet wurde.
5. Skelett von @RestClientTest und Eigenschaften
@RestClientTest hat eine angenehme Eigenschaft: Der Test sieht fast wie ein normaler Spring Boot Test aus, aber unter der Haube wird ein schmaler Kontext hochgefahren. Das bedeutet, der Start ist schnell und die Abhängigkeiten sind kontrollierbar. Für Anfänger ist dies ein ausgezeichneter Kompromiss: Sie sind immer noch in der vertrauten Spring-Welt (@Autowired, Properties, Autokonfiguration), aber Sie ertrinken nicht in der gesamten Anwendung.
Beginnen wir mit dem Skelett von RestClientModerationClientTest. Wichtig sind zwei Injektionen: der RestClientModerationClient selbst und der MockRestServiceServer. Der Client ist unser Testobjekt, der Server ist der Stellvertreter für das externe Netzwerk.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.client.MockRestServiceServer;
@RestClientTest(RestClientModerationClient.class) // Hebt nur den Test-Slice für den HTTP-Client an
@TestPropertySource(properties = {
// Legt baseUrl in den Testkontext, damit RestClient die vollständige URL korrekt aufbaut.
"contenthub.moderation.base-url=https://moderation.local"
})
class RestClientModerationClientTest {
@Autowired
private RestClientModerationClient client; // Testobjekt (unser Adapter)
@Autowired
private MockRestServiceServer server; // Abfangjäger für HTTP-Anfragen von RestClient (ohne reales Netzwerk)
}
Aber wir übergeben baseUrl an den Konstruktor als String. Also müssen wir zeigen, woher er kommt. Eine einfache lehrreiche Möglichkeit ist @Value("${contenthub.moderation.base-url}"). Im Produktionscode würden Sie dies wahrscheinlich als Properties-Klasse gestalten, aber die Testmechanik ändert sich dadurch nicht.
Minikonstruktor mit @Value:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.client.RestClient;
public RestClientModerationClient(
RestClient.Builder builder,
@Value("${contenthub.moderation.base-url}") String baseUrl // nimmt baseUrl aus der Konfiguration
) {
// Erstellt RestClient mit Basisadresse, sodass im Code relative URIs ("/api/check") verwendet werden können.
this.restClient = builder.baseUrl(baseUrl).build();
}
So wird die Testeigenschaft aus @TestPropertySource tatsächlich verwendet. Das ist ein Detail, aber wichtig: Wenn Sie vergessen, base-url bereitzustellen, beginnt der Adapter, URLs aus der Luft zu bauen (und Luft ist eine schlechte Konfigurationsquelle – sie ist zu leichtsinnig).
6. Prüfung des Vertrags und des Mappings
Happy Path: OK
Jetzt kommt der schönste Teil: Wir schreiben einen Test, der sich wie ein Szenario liest. Wir definieren Erwartungen für MockRestServiceServer, rufen dann client.moderate(...) auf und machen AssertJ-Überprüfungen des Ergebnisses.
Angenommen, der externe Dienst gibt folgendes JSON zurück:
{"result":"OK","reason":null}
Der Test könnte so aussehen:
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
class RestClientModerationClientTest {
// ... @Autowired Felder
@Test
void shouldMapOkResponse() {
// Sage dem Mock-Server: Erwarte eine bestimmte Anfrage (URL + Methode)...
server.expect(requestTo("https://moderation.local/api/check"))
.andExpect(method(POST))
// ...und gib ein vordefiniertes JSON zurück, als wäre es die Antwort des echten Dienstes.
.andRespond(withSuccess("""
{"result":"OK","reason":null}
""", APPLICATION_JSON));
// Rufe den Adapter wie normalen Code auf: Er "glaubt", er geht nach außen.
ModerationDecision decision = client.moderate(
new ModerationRequest("Hello", "Some body")
);
// Prüfe das Domänenergebnis, nicht das "rohe" JSON.
assertThat(decision.result()).isEqualTo(ModerationResult.OK);
assertThat(decision.reason()).isNull();
}
}
Hier sichern wir bereits zwei Risikoschichten ab: Wir stellen fest, dass die Anfrage genau an https://moderation.local/api/check ging und dass die Antwort korrekt in eine Domänenentscheidung umgewandelt wurde. Für Anfänger ist das der Schlüsselgedanke: Ein Adaptertest ist nicht „prüfen, ob OK zurückkommt“, sondern „prüfen, ob der Adapter korrekt nach Vertrag kommuniziert“.
Beachten Sie """...""". Java-Textblöcke sind eine hervorragende Möglichkeit, JSON direkt im Test ohne endloses Escaping zu halten. Ja, es sieht aus wie eine „Mini-JSON-Datei“, nur ohne Dateisystem. In kleinen Beispielen ist das angemessen, wenn das Payload komplexer wird, sollte man zu Fixture-Dateien übergehen (das ist jedoch eine separate Disziplin; wir wollen jetzt keinen „Krieg der Formate“ anzetteln).
Vertrag der Anfrage: Header und Body
Manchmal schreiben Studenten einen solchen Test und sind zufrieden. Aber dann stellt sich heraus, dass der Adapter versehentlich GET statt POST sendet (oder gar keinen Body sendet, weil „ups, .body(request) vergessen“) und der Test trotzdem grün ist. Um nicht in diese Falle zu tappen, kann man ein paar Prüfungen auf die Anfrage hinzufügen.
Die praktischste Prüfung ist sicherzustellen, dass wir tatsächlich JSON gesendet haben. MockRestServiceServer erlaubt das Matchen des Request-Body. Man sollte es nicht zu einem totalen Snapshot des gesamten JSON machen (sonst wird der Test spröde), aber die Schlüsselfelder zu prüfen, ist sinnvoll.
import org.junit.jupiter.api.Test;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
@Test
void shouldSendJsonBody() {
server.expect(requestTo("https://moderation.local/api/check"))
.andExpect(method(POST))
// Prüfen, dass der Content-Type Header korrekt gesetzt ist.
.andExpect(content().contentType(APPLICATION_JSON))
// Prüfen der Form der Anfrage: Schlüsselfelder, die für den externen Vertrag wichtig sind.
.andExpect(content().json("""
{"title":"Hello","body":"Some body"}
"""))
.andRespond(withSuccess("""
{"result":"OK","reason":null}
""", APPLICATION_JSON));
ModerationDecision decision = client.moderate(
new ModerationRequest("Hello", "Some body")
);
// Hier reicht es, das Ergebnis zu prüfen: Die Anfrage wurde bereits durch die obigen Erwartungen festgehalten.
assertThat(decision.result()).isEqualTo(ModerationResult.OK);
}
Hier haben wir bereits einen viel „vertraglicheren“ Aspekt festgehalten: die Form der Anfrage. Wenn morgen jemand das Feld body in text in ModerationRequest umbenennt, wird der Test sofort anschlagen. Und das ist gut, denn der externe Dienst ist nicht verpflichtet, unsere Absichten zu erraten; er kann nur das lesen, was wir ihm senden.
Wichtig ist, nicht zu übertreiben. Wenn Sie anfangen, das gesamte JSON bis zum letzten Komma zu vergleichen, kommen Sie schnell zu Tests, die wegen kosmetischer Änderungen brechen. Im Adapter sollte man normalerweise das prüfen, was tatsächlich Teil des externen Vertrags ist: URL, Methode, Content-Type, Schlüsselfelder.
Antwortzweige: WARN und BLOCK
Ein sehr häufiger Anfängerfehler ist der Wunsch, „einen großen Test für alle Fälle“ zu schreiben. Er wirkt heldenhaft, fällt aber tragisch aus: Jeder Defekt bricht das gesamte Szenario, und Sie müssen lange herausfinden, welcher Zweig genau kaputt ist.
Viel stabiler ist es, „ein Zweig – ein Test“ beizubehalten. Dann ist der Test eine Dokumentation: So sieht OK aus, so BLOCK. Für unsere Moderation ist BLOCK besonders wichtig: Der Grund der Sperrung muss bis zur Domäne und von dort zur Geschäftslogik gelangen.
import org.junit.jupiter.api.Test;
@Test
void shouldMapBlockResponse() {
// BLOCK-Zweig: Wichtig, dass der Grund beim Mapping nicht verloren geht.
server.expect(requestTo("https://moderation.local/api/check"))
.andRespond(withSuccess("""
{"result":"BLOCK","reason":"spam"}
""", APPLICATION_JSON));
ModerationDecision decision = client.moderate(
new ModerationRequest("Buy now", "Cheap cheap cheap")
);
assertThat(decision.result()).isEqualTo(ModerationResult.BLOCK);
assertThat(decision.reason()).isEqualTo("spam");
}
Analog kann man WARN festhalten:
import org.junit.jupiter.api.Test;
@Test
void shouldMapWarnResponse() {
// WARN-Zweig: Das Ergebnis soll in die Domäne gelangen, die Erläuterung erhalten bleiben.
server.expect(requestTo("https://moderation.local/api/check"))
.andRespond(withSuccess("""
{"result":"WARN","reason":"too many links"}
""", APPLICATION_JSON));
ModerationDecision decision = client.moderate(
new ModerationRequest("Links", "http://a http://b http://c")
);
assertThat(decision.result()).isEqualTo(ModerationResult.WARN);
assertThat(decision.reason()).isEqualTo("too many links");
}
Wir halten uns bewusst auf dem Happy Path: HTTP-Antwort erfolgreich (200 OK), JSON gültig. Wichtig ist, den grundlegenden, schmalen und klaren Test zu fixieren, der den externen Vertrag des Adapters ohne überflüssige Infrastruktur hält. Wenn dieser Pfad stabil ist, kann man darauf leicht die Fehlerzweige desselben Clients aufsetzen.
7. Lesbarkeit und Stabilität der Tests
Wenn die Anzahl der Tests drei übersteigt, entsteht ein neues Risiko: Die Tests beginnen auszusehen wie ein „Bettlaken aus server.expect(...)“. Und dann ist es sehr leicht, in dem Versuch, „Code zu verkürzen“, den Sinn zu verstecken. Das haben wir bereits bei MockMvc und bei Unit-Tests gesehen: Helfer sind nützlich, solange sie den Test nicht in ein Rätsel verwandeln.
Ein guter praktischer Punkt ist, wiederkehrende Teile auszulagern, die keinen Szenario-Sinn haben, aber die Schlüsselunterschiede lesbar zu lassen. Zum Beispiel kann man die URL als Konstante definieren. Das ist eine kleine Sache, aber sie schützt vor „Tippfehlern für eine Million Dollar“ (oder vielmehr eine Million Minuten Debugging).
private static final String CHECK_URL = "https://moderation.local/api/check";
Dann kann man einen kleinen Helfer für die Antwort formulieren:
private void stubResponse(String json) {
server.expect(requestTo(CHECK_URL))
.andRespond(withSuccess(json, APPLICATION_JSON));
}
Im Test bleibt dann nur noch der Sinn:
@Test
void shouldMapOkResponse() {
stubResponse("""{"result":"OK","reason":null}""");
ModerationDecision decision = client.moderate(
new ModerationRequest("Hello", "Some body")
);
assertThat(decision.result()).isEqualTo(ModerationResult.OK);
}
Aber es ist wichtig, nicht zu weit zu gehen und zum Beispiel die HTTP-Methode oder die Prüfung des Request-Body zu verstecken. Denn später vergessen Sie, dass überhaupt die POST-Methode geprüft wird, und der Test wird zu „irgendwie prüft er etwas, aber was genau – Geheimnis der Alten“. In Adaptertests ist es entscheidend, die Transparenz des externen Vertrags zu bewahren.
Eine weitere nützliche Technik, besonders für Anfänger, ist das Gruppieren von Zweigen mit @Nested. Dann liest sich die Testklasse wie eine Minidokumentation des Protokolls:
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
@Nested
class Mapping {
@Test
void ok() { /* ... */ }
@Test
void warn() { /* ... */ }
@Test
void block() { /* ... */ }
}
Das macht Tests nicht „magisch besser“, aber es erhöht die Lesbarkeit drastisch. Und Lesbarkeit ist das, was Sie in drei Monaten rettet, wenn Sie sich nicht mehr erinnern, warum Sie diesen Test überhaupt geschrieben haben, er aber plötzlich im CI fehlschlägt.
Und eine letzte Nuance: Denken Sie daran, dass requestTo(...) in den Erwartungen meistens die vollständige URL sein sollte, wenn Sie baseUrl verwenden. Das ist einer der häufigsten Gründe für Fehlschläge. Sie schreiben eine Erwartung auf "/api/check", aber die tatsächliche Anfrage geht an "https://moderation.local/api/check". Und MockRestServiceServer sagt ehrlich: „passt nicht“. Es ist nicht böswillig – es ist einfach wörtlich. Wie jedes gute Testwerkzeug.
8. Typische Fehler bei @RestClientTest
Fehler Nr. 1: Den Adapter mit @SpringBootTest testen, weil „das zuverlässiger ist“.
Das klingt logisch, bis man den Preis bemerkt. Der volle Kontext macht den Test langsam, erfordert oft mehr Konfiguration und liefert eine schlechtere Diagnose: Der Fehlschlag kann an allem Möglichen liegen. @RestClientTest wurde genau dafür geschaffen, den Vertrag des Adapters schnell und isoliert zu prüfen, ohne jede Überprüfung in einen Min-Start der gesamten Anwendung zu verwandeln.
Fehler Nr. 2: Nur das Ergebnis prüfen (decision.result()), aber den externen Vertrag nicht prüfen.
Wenn Sie URL, Methode und zumindest die minimale Form des Request-Body nicht festhalten, kann der Test trotz eines schwerwiegenden Fehlers grün werden: Zum Beispiel, wenn der Adapter zu einem anderen Endpunkt geht oder „versehentlich“ GET statt POST sendet. Der Wert des Adaptertests liegt darin, dass er die Vereinbarung zwischen Ihrem Code und dem externen Dienst schützt, nicht nur Ihr Enum innerhalb der JVM.
Fehler Nr. 3: In requestTo(...) den Pfad statt der vollständigen URL erwarten, wenn baseUrl verwendet wird.
Das ist einer der nervigsten Fehler, weil er „wie eine Kleinigkeit aussieht“, aber den gesamten Test zerstört. Wenn Sie RestClient über builder.baseUrl("https://moderation.local") aufbauen, geht die tatsächliche Anfrage an die vollständige Adresse. Und MockRestServiceServer erwartet ebenfalls die vollständige Adresse, sonst versteht es nicht, was genau Sie prüfen wollten.
Fehler Nr. 4: Den Test zu einer Überprüfung „jedes Kommas“ machen und ihn spröde machen.
Ja, man kann das gesamte JSON der Anfrage/Antwort exakt vergleichen. Aber dann führt jede Kosmetik (z.B. Reihenfolge der Felder, Auftreten eines neuen optionalen Feldes) dazu, dass Tests fehlschlagen, selbst wenn sich der Geschäftssinn nicht geändert hat. Halten Sie die Balance: Prüfen Sie das, was wirklich Vertrag ist, und machen Sie den Test nicht zu einem „Screenshot“ der gesamten Struktur ohne Notwendigkeit.
Fehler Nr. 5: In einem Test die Verantwortung des Adapters und die Verantwortung des Services vermischen.
Wenn der Test beginnt zu prüfen „dass die Anfrage korrekt ist, der Artikel den Status geändert hat, die Security keinen Anonymen gelassen hat und die Benachrichtigung gesendet wurde“, erhalten Sie wieder ein teures, langes Szenario mit schlechter Diagnose. Den Adapter prüfen wir im @RestClientTest, die Geschäftsreaktion des Services im Unit-Test mit fake/stub des Ports, die übergeordneten Effekte in Integrationstests. Jede Schicht sollte ihren Teil der Wahrheit beweisen, sonst wird die Test-„Wahrheit“ zu einem Brei.
GO TO FULL VERSION