Integrationstests von Java-Anwendungen mit Docker
Moderne Java-Entwicklung ist ohne qualitativ hochwertiges Testen undenkbar. Unit-Tests sind jedoch oft nicht in der Lage, Probleme zu erkennen, die bei der Interaktion von Komponenten mit echten Datenbanken, Message Queues oder externen APIs auftreten. Hier kommen Integrationstests ins Spiel, und Docker macht diesen Prozess einfach, reproduzierbar und isoliert. In diesem Artikel werden wir uns ansehen, wie man Docker mit der Bibliothek Testcontainers und Docker Compose effektiv für Integrationstests von Java-Anwendungen einsetzt.
Warum Docker die Spielregeln bei Integrationstests ändert
Der traditionelle Ansatz für Integrationstests erfordert oft die Installation und Konfiguration aller Abhängigkeiten (Datenbanken, Message Broker) auf dem lokalen Entwicklerrechner oder auf dem CI-Server. Dies führt zu einer Reihe von Problemen:
- Instabile Umgebungen: Unterschiedliche DBMS-Versionen, Portkonflikte, Unterschiede zwischen Entwicklungs- und Produktionsumgebung.
- Komplexe CI/CD-Einrichtung: Manuelle Installation und Konfiguration von Diensten in der Pipeline erforderlich.
- Datenverschmutzung: Testdaten können in der lokalen Datenbank verbleiben und nachfolgende Testläufe beeinflussen.
- Parallelisierung: Gleichzeitige Testausführung auf einem Rechner kann zu Konflikten führen.
Docker löst diese Probleme, indem es leichtgewichtige, isolierte und flüchtige Container bereitstellt. Jeder Test (oder jede Testgruppe) kann seine eigene Instanz von PostgreSQL, Redis oder Kafka starten und nach dem Test wieder zerstören. Dies garantiert saubere und wiederholbare Ergebnisse.
Projekteinrichtung: Testcontainers vs Docker Compose
Es gibt zwei Hauptansätze, um Docker in Java-Tests zu integrieren. Wir betrachten beide.
1. Testcontainers: Bibliothek zur Containerverwaltung aus dem Code
Testcontainers ist eine Java-Bibliothek, mit der Sie den Lebenszyklus von Docker-Containern direkt aus Ihren Tests heraus verwalten können. Es ist der De-facto-Standard für Integrationstests im Spring-Boot-Ökosystem und darüber hinaus.
Vorteile:
- Programmatische Steuerung: Container werden automatisch gestartet und gestoppt.
- Unterstützung für gängige Dienste "out of the box" (GenericContainer, PostgreSQLContainer, KafkaContainer usw.).
- Automatische Auswahl freier Ports (vermeidet Konflikte).
- Ideal für modulare Integrationstests.
Beispiel: Testen eines REST-Controllers mit PostgreSQL
Fügen Sie die Abhängigkeit zu Ihrer pom.xml (Maven) hinzu:
<dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>1.19.3</version> <scope>test</scope></dependency><dependency> <groupId>org.testcontainers</groupId> <artifactId>postgresql</artifactId> <version>1.19.3</version> <scope>test</scope></dependency>Jetzt schreiben wir einen Test für eine Spring Boot Anwendung, die mit PostgreSQL arbeitet:
import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.boot.test.web.client.TestRestTemplate;import org.springframework.http.ResponseEntity;import org.testcontainers.containers.PostgreSQLContainer;import org.testcontainers.junit.jupiter.Container;import org.testcontainers.junit.jupiter.Testcontainers;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnviron
```