Kleiner Bruder API
https://bildung.social/@oerinformatik/
https://oer-informatik.de/sbb02_kleiner-bruder-api
tl/dr; _(ca. 6 min Lesezeit): Wir implementieren eine API, die wie ein kleiner Bruder antwortet: Allem gesagten wird “Selber” vorangestellt. An diesem dämlichen Beispiel lernen wir einen Controller für GET-Requests und das Unit-Test-Framework jUnit kennen. Dieser Artikel ist ein Teil der Artikelserie zu einem Adressbuch-SpringBoot-Projekt. Weiter geht es dann wieder mit dem eigentlichen Projekt. Zuerst ein Modell für die Datenhaltung: das Modell zur Verarbeitung von Adressen.
Implementieren der API
Das “Hello World” ist ja schon ein Anfang. Bevor wir beginnen, die eigentliche API zu erstellen, möchte ich aber mit einer kleinen Spielerei weiter Springboot kennenlernen.
Jeder kennt das Kindergartenspiel, bei dem das eine Kind etwas (oft beleidigendes) sagt und das andere Kind diese Zuweisung mit vorangestelltem “Selber” wiederholt:
- Du Schaf!
- Selber Schaf!
Derlei Spiele werden mit Begeisterung von kleinen Brüdern aufgenommen, wir werden die API, die genau das umsetzen soll, also “kleiner Bruder API” nennen.
Die Klasse “AddressbookApplication.java” ist noch vom “Hello World”-Beispiel als @RestController
annotiert, sie reagiert also auf HTTP-Requests und sucht für eingegebene Pfade (Routen) zugehörige Methoden. Wir fügen für die Route http://localhost:8085/kleinerBruder
eine neue Methode ein:
@RequestMapping("/kleinerBruder/{zuweisung}")
public String selberAntwort(@PathVariable String zuweisung) {
return "Selber "+zuweisung;
}
Hier ist allerdings die Besonderheit, dass die Route einen Parameter enthält ({zuweisung}
), den wir auch als Parameter an die Methode übergeben (durch die Annotation @PathVariable
gekennzeichnet). Wir können also die Zeichenkette, die nach dem letzten “/” in der URL übergeben wird in der String-Variablen zuweisung
nutzen:

Get-Requests näher untersuchen
Wir können mit dem HTTP-GET-Request auf der Ressource “localhost:8085/kleinerBruder/…” (nichts anderes ist der Aufruf im Browser) also eine parametrisierte Antwort erhalten und direkt im Browser als Text zurückgeben lassen.
In einem Linux/MacOSX-Terminal mit installiertem curl
lässt sich dieser Get-Request ebenso abschicken - und zudem das ganze etwas besser testen, da wir bei Bedarf mehr Informationen zurück geliefert bekommen:
Um unter Windows über die PowerShell Get-Requests abzusetzen dient das Commandlet Invoke-WebRequest
(curl
wird auch erkannt, ist hier ein Alias für CmdLets Invoke-WebRequest
und muss entsprechend parametrisiert werden):
StatusCode : 200
StatusDescription :
Content : Selber Hornochse
RawContent : HTTP/1.1 200
Keep-Alive: timeout=60
Connection: keep-alive
Content-Length: 16
Content-Type: text/plain;charset=UTF-8
Date: Mon, 05 Sep 2022 10:58:02 GMT
Selber Hornochse
Forms : {}
Headers : {[Keep-Alive, timeout=60], [Connection, keep-alive], [Content-Length, 16], [Content-Type,
text/plain;charset=UTF-8]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : mshtml.HTMLDocumentClass
RawContentLength : 16
Testen der API
Test vorbereiten
Anhand dieser kleinen API soll direkt eine Testumgebung erstellt werden. Das Framework hat hierzu bereits unter src/test/...
eine jUnit-Testklasse vorbereitet, die wir nur noch mit Leben füllen müssen. Wir können über die Datei- oder über die Projektansicht zu den Testklassen navigieren.

Als Voraussetzung für den Test benötigen wir ein Umfeld, in dem unsere HTTP-Requests aufgerufen werden (der Test soll ja unabhängig von Browsern oder curl
-Aufrufen automatisch laufen). Hierzu benötigen wir ein Objekt der Klasse WebApplicationContext
, das unsere App repräsentiert. Über die Annotation @Autowired
weiß das SpringBoot-Framework, dass es selbst die zugehörigen Instanzen dieser Klassen erzeugen und verknüpfen muss. Ferner brauchen wir ein Objekt der Klasse MockMvc
, über das wir die HTTP-Aufrufe simulieren. Dessen Instanziierung folgt im nächsten Abschnitt.
Die Tests können erst durchgeführt werden, wenn das Mockobjekt instanziiert ist. Bei jedem Test soll eine eigene Instanz von MockMvc
erzeugt werden. Mit Hilfe der jUnit5-Annotation @BeforeEach
wird die Methode setup()
vor jedem Test ausgeführt und erzeugt eine neue Instanz von MockMvc
:
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
(Hinweis: falls Sie auf alte Beispiele stoßen, die mit @Before
annotiert sind, so handelt es sich um jUnit4-Tests. Diese sind nicht kompatibel mit dem neueren jUnit5!)
Aufbau der Testfälle
Es ist üblich Testfälle in einzelne Schritte aufzuteilen, etwa in die Vorbereitung der Testdaten, das Ausführen der Methodenaufrufe und das Vergleichen der Ergebnisse mit den Erwartungen. Martin Fowler definiert die Phasen given, when, then folgendermaßen: 1:
The given part describes the state of the world before you begin the behavior you’re specifying in this scenario. You can think of it as the pre-conditions to the test.
The when section is that behavior that you’re specifying.
Finally the then section describes the changes you expect due to the specified behavior.
Auf das Beispiel zugeschnitten heißt das:
- In der given -Sektion werden alle Vorbedingungen gesetzt. Erforderliche Instanzen erzeugt, Variablen gesetzt, Dienste gestartet. In unserem Fall wird hier nur festgelegt, dass der mit dem Pfad (der URI
http://localhost:8085/kleinerBruder/...
) übergebene Wert “Hornochse” ist:
- In der
when
-Sektion wird der gemockte HTTP-Aufruf (ein GET-Request) vorbereitet und in der VariabletestRequest
gespeichert.
- In der
then
-Sektion wird der erwartete Rückgabewert definiert (expected
). Dann wird in der Mocking-Umgebung (mockMVC
) der vorbereitete Request abgesetzt (.perform()
) und der Rückgabewert mit dem erwarteten Wert verglichen (.andExpect()
).
ResultMatcher expected = MockMvcResultMatchers.status().isOk();
mockMvc.perform(testRequest).andExpect(expected);
Nach dieser Logik erstellen wir zwei Tests: einen, der überprüft, ob der HTTP-StatusCode 200 (Ok) zurückgegeben wird und einen zweiten, der den Inhalt der Antwort überprüft:
@Test
public void testSelberAntwort_httpStatusIsOK() throws Exception {
// given
String pathparam = "Hornochse";
// when
MockHttpServletRequestBuilder testRequest = MockMvcRequestBuilders.get("/kleinerBruder/"+pathparam);
// then
ResultMatcher expected = MockMvcResultMatchers.status().isOk();
mockMvc.perform(testRequest).andExpect(expected);
}
@Test
public void testSelberAntwort_contentLoads() throws Exception {
// given
String pathparam = "Hornochse";
// when
MockHttpServletRequestBuilder testRequest = MockMvcRequestBuilders.get("/kleinerBruder/"+pathparam);
// then
ResultMatcher expected = MockMvcResultMatchers.content().string("Selber "+pathparam);;
mockMvc.perform(testRequest).andExpect(expected);
}
Was fehlt sind noch die nötigen Imports. In der Regel lassen diese sich über die Tooltips automatisch finden. Im Ganzen sollte es am Ende etwa so aussehen:
package de.csbme.ifaxx.addressbook;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
@SpringBootTest
class AddressbookApplicationTests {
@Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
@BeforeEach
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void testSelberAntwort_httpStatusIsOK() throws Exception {
// given
String pathparam = "Hornochse";
// when
MockHttpServletRequestBuilder testRequest = MockMvcRequestBuilders.get("/kleinerBruder/"+pathparam);
// then
ResultMatcher expected = MockMvcResultMatchers.status().isOk();
mockMvc.perform(testRequest).andExpect(expected);
}
@Test
public void testSelberAntwort_contentLoads() throws Exception {
// given
String pathparam = "Hornochse";
// when
MockHttpServletRequestBuilder testRequest = MockMvcRequestBuilders.get("/kleinerBruder/"+pathparam);
// then
ResultMatcher expected = MockMvcResultMatchers.content().string("Selber "+pathparam);
mockMvc.perform(testRequest).andExpect(expected);
}
}
Jetzt können die Tests ausgeführt werden: Im einfachsten Fall, in dem die Quelltextdatei der Tests geöffnet und ausgeführt wird (Ausführen - Debugging starten / F5 / Play-Symbol). Das Ergebniss wird etwa so dargestellt:

Um sicher zu gehen, dass die Tests funktionieren, sollten ruhig versuchsweise Änderungen an dem Code vorgenommen werden, die zu scheiternden Tests führen müssten.
Nächster Schritt
Dieser Artikel ist ein Teil der Artikelserie zu einem Adressbuch-SpringBoot-Projekt.
Weiter geht es dann wieder mit dem eigentlichen Projekt. Zuerst ein Modell für die Datenhaltung: das Modell zur Verarbeitung von Adressen.
Hinweis zur Nachnutzung als Open Educational Resource (OER)
Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY-SA 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: “Kleiner Bruder API” von Hannes Stein, Lizenz: CC BY-SA 4.0. Der Artikel wurde unter https://oer-informatik.de/sbb02_kleiner-bruder-api veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/java-springboot/Backend. Stand: 04.09.2023.
[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]