Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Testcontainers Kata

Einleitung

Testcontainers ist eine Bibliothek für die Nutzung echter Abhängigkeiten wie Datenbanken, Message Broker o.ä. in Integrationstests. Die abhängigen Services werden für die Tests automatisch als Docker-Container gestartet, und ersetzen fehleranfällige Mocks oder In-Memory-Datenbanken durch eine produktionsnahe Umgebung, was eine isolierte Ausführung der Tests ermöglicht.

In diesem Code-Kata befinden sich Aufgaben, anhand derer du dich mit Testcontainers vertraut machen kannst.

Voraussetzungen

Für das Kata benötigst du auf deinem Rechner Installationen von:

  • Java 21 (oder höher)
  • Maven 3.9 (oder höher)
  • Docker
  • einer IDE mit Debugger

Grundlegende Docker- und Spring-Boot-Kenntnisse sollten vorhanden sein.

Die folgenden Frameworks werden im Kata verwendet:

  • Testcontainers 2
  • Spring Boot 4
  • JUnit 6
  • AssertJ
  • REST-assured

Die Anwendung

Bei der Anwendung handelt es sich um das Backend für ein Bücherei-Verwaltungssystem. Über eine REST-API (implementiert im BookController) können Bücher abgefragt oder gelöscht werden.

Mit den folgenden Befehlen kann die Anwendung in einem Terminal gestartet werden:

# PostgreSQL-DB und Kafka in Docker starten
$ docker compose -f docker/docker-compose.yml up -d

# Anwendung starten
$ mvn spring-boot:run 

Nun können die Bücher abgefragt werden, z.B. durch Öffnen der URL http://localhost:8080/api/books in einem Browser, per curl-Anfrage, etc.

In der Kafbat-UI können die nach Kafka geschriebenen Nachrichten eingesehen werden. Sie ist verfügbar unter http://localhost:8090/ui/.

Aufgaben

Führe Testcontainers in deine Tests ein, indem du nach und nach die folgenden Schritte abarbeitest:

1. Integrationstest für BookRepository

Implementiere den Test in BookRepositoryTest.java mit einem PostgreSQL-Testcontainer. Verwende das Image postgres:18-alpine. Richte dich dabei nach der Anleitung aus der Testcontainers-Doku, ab dem Abschnitt Write test for API endpoint. Zunächst testen wir nur das Repository. Der Test für den API-Endpoint (BookController) kommt später.

Hinweise

  • Die offizielle Testcontainers-Dokumentation gilt derzeit noch für Version 1.
  • Da dieses Kata Testcontainers 2 nutzt, sieht der zu schreibende Code geringfügig anders aus als in der Dokumentation.
  • Die Produktionsklassen, Dependencies, SQL-Skripte sowie die Testklassen sind bereits vorhanden und müssen nicht mehr hinzugefügt werden.

Die Aufgabe ist erledigt, wenn der Test erfolgreich durchläuft.

2. Container prüfen

Setze einen Breakpoint in der Testmethode, und starte den Test im Debug-Modus. Wenn der Debugger am Breakpoint anhält, schaue dir an, welche Docker-Container gerade laufen. Zum Beispiel via Ausführung von docker ps in einem Terminal, oder im Services-Toolwindow von IntelliJ.

  • Schaue dir den Port an, auf den der PostgreSQL-Container lokal gemappt ist. Überlege, weshalb dies nicht der Standardport von PostgreSQL ist.
  • Zusätzlich sollte ein weiterer Container laufen, namens testcontainers-ryuk-.... Schlage nach oder frage eine KI deiner Wahl, was es damit auf sich hat.
  • Setze den Test fort und prüfe die laufenden Container nach Ende des Testprozesses.

3. JUnit-Erweiterung von Testcontainers nutzen

Anstatt den Container "manuell" in den JUnit-Lifecycle-Methoden @BeforeAll und @AfterAll zu starten und zu stoppen, sollen jetzt die Annotationen @Testcontainers und @Container verwendet werden. Siehe dazu Using JUnit 5 Extension Annotations.

Teste dies mit dem BookRepositoryTest.

4. PostgreSQL-Container via JDBC-URL starten

Ein Datenbank-Container kann auch ohne Modifikation des Java-Codes gestartet werden, indem man eine spezielle JDBC-URL konfiguriert. Siehe Testcontainers-Dokumentation JDBC support.

Kommentiere die Definition des PostgreSQL-Containers in der Testklasse sowie alle zugehörigen Codestellen aus. Vergiss dabei nicht, auch die Annotationen @Testcontainers und @Container auszukommentieren. Der Test sollte nun wieder fehlschlagen.

Ergänze src/test/resources/application.properties um die folgende Zeile:

spring.datasource.url=jdbc:tc:postgresql:18-alpine:///test

Starte den Test neu. Prüfe zur Laufzeit (mit Breakpoint), ob der Container läuft. Wenn dies der Fall ist und der Test erfolgreich durchläuft, ist die Aufgabe erledigt.

5. PostgreSQL-Container wiederverwenden

Entferne die JDBC-URL-Konfiguration aus den Test-Properties wieder, oder kommentiere sie aus. Führe den BookControllerTest aus. Er wird fehlschlagen.

Definiere in der Klasse AbstractIntegrationTest den PostgreSQL-Container mit Modifier protected, die configureProperties()-Methode, sowie die beforeAll()- und afterAll()-Methoden, die den Container starten und stoppen, gemäß Doku.

Führe den BookControllerTest und den BookRepositoryTest aus. Sie sollten jetzt beide grün sein.

Setze Breakpoints in jeweils einer Testmethode der beiden Testklassen, und führe die Tests aus. Achte darauf, dass beide in einem gemeinsamen Prozess laufen (z.B. alle Tests im Package de.doubleslash.kata.testcontainers.db starten). Prüfe jeweils die laufenden Container, wenn der Debugger an den Breakpoints anhält. Achte auf die Namen der Container. Was fällt dir auf?

6. Singleton-Containers-Pattern anwenden

Schau dir die Dokumentation zum Singleton Containers Pattern an.

Setze das Singleton-Container-Pattern für den PostgreSQL-Container in der Klasse AbstractIntegrationTest um. Den Kafka-Container kannst du fürs Erste ignorieren.

Starte beide Tests wie in der vorherigen Aufgabe in einem gemeinsamen Prozess im Debug-Modus und lass sie an den Breakpoints anhalten. Prüfe an beiden Stellen wieder die Container-Namen. Was beobachtest du?

7. Kafka-Container verwenden

Füge dem AbstractIntegrationTest einen KafkaContainer namens kafka mit dem Image apache/kafka-native:4.3.1 hinzu. Damit es keinen Kompilierfehler gibt, muss der Container mit dem Modifier ´protected´ deklariert werden. Ergänze außerdem den start()-Aufruf für kafka, analog zum PostgreSQL-Container. Füge in der Methode configureProperties die Konfiguration für spring.kafka.bootstrap-servers hinzu. Die Bootstrap-Server kannst du von der Kafka-Containerinstanz abfragen.

Entferne in der Klasse BookDeleteEventTest den Kommentar in der Methode getKafkaConfigMap(). Starte den Test. Er sollte nun den Kafka-Container nutzen und erfolgreich durchlaufen. Arbeite auch hier wieder mit einem Breakpoint und schaue dir die Container zur Laufzeit sowie nach Beendigung des Tests an.

8. Generic Container verwenden

Mit GenericContainer können Test-Container für alle Docker-Dienste erstellt werden, für die es keine spezialisierte Testcontainers-Containerklasse gibt.

Füge der Klasse AbstractIntegrationTest einen GenericContainer mit Image mailhog/mailhog:v1.0.1, sowie die start()-Anweisung hinzu. Starte einen der Tests, setze einen Breakpoint und prüfe, ob der Container gestartet wurde. Schau dir die Ports des Containers an.

Du wirst feststellen, dass die Service-Ports nicht nach außen gemappt sind, da der GenericContainer die Standardports des Dienstes im Container nicht kennt, im Gegensatz zu spezialisierten Klassen (z.B. 5432 für PostgreSQLContainer).

Daher müssen wir dem GenericContainer noch mitteilen, welche Ports er mappen soll. Das geht mit dem Aufruf .withExposedPorts(1025, 8025), das man direkt hinten an den Konstruktor-Aufruf anhängt.

Starte den Test nach dieser Ergänzung erneut und schau dir die Ports des Containers noch einmal an.

9. Gemappten Port abfragen

Mit .getMappedPort(originalPort) kann man den Zufallsport ermitteln, auf den der ursprüngliche Port gemappt ist. So kann man z.B. den getesteten Service konfigurieren, sodass dieser den gemappten Port nutzt. Der Container muss beim Aufruf der Methode gestartet sein, d.h. der Aufruf von start() muss davor erfolgen.

Finde den Zufallsport heraus, auf den der Port 8025 des MailHog-Containers gemappt ist, und gib diesen auf der Konsole aus.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages