Integrationstests von Java mit Docker: Von A bis Z
\n\nIntegrationstests sind eine entscheidende Entwicklungsphase, die es ermöglicht, die Interaktion von Anwendungskomponenten mit externen Systemen wie Datenbanken, Nachrichtenwarteschlangen und REST-Diensten zu überprüfen. Der traditionelle Ansatz mit H2 oder eingebetteten Servern führt oft zu Abweichungen zwischen Test- und Produktionsumgebungen. Die Lösung ist Docker. In diesem Artikel erläutern wir, wie Sie Integrationstests von Java-Anwendungen mit Docker unter Verwendung der Bibliothek Testcontainers und Docker Compose einrichten. Sie erfahren, wie Sie Tests schreiben, die mit echten Containern arbeiten, und wie Sie diese in CI/CD integrieren.
\n\nWarum Docker für Integrationstests?
\nDocker ermöglicht es, isolierte Container mit genauen Versionen von Abhängigkeiten (PostgreSQL, Redis, Kafka) direkt vor den Tests zu starten und danach zu zerstören. Dies garantiert:
\n- \n
- Identität mit der Produktion — Sie testen mit einer echten Datenbank, nicht mit einer Emulation. \n
- Isolation — jeder Testlauf beginnt mit einem sauberen Zustand. \n
- Parallele Ausführung — mehrere Testklassen können verschiedene Container verwenden. \n
- Reproduzierbarkeit — Tests funktionieren auf dem lokalen Rechner und in CI gleichermaßen. \n
Projekteinrichtung mit Testcontainers
\nTestcontainers ist eine Java-Bibliothek, die den Lebenszyklus von Docker-Containern verwaltet. Fügen wir die Abhängigkeit in pom.xml (Maven) hinzu:
<dependency>\n <groupId>org.testcontainers</groupId>\n <artifactId>testcontainers</artifactId>\n <version>1.19.3</version>\n <scope>test</scope>\n</dependency>\n<dependency>\n <groupId>org.testcontainers</groupId>\n <artifactId>postgresql</artifactId>\n <version>1.19.3</version>\n <scope>test</scope>\n</dependency>\n<dependency>\n <groupId>org.springframework.boot</groupId>\n <artifactId>spring-boot-starter-test</artifactId>\n <scope>test</scope>\n</dependency>\n\nFür Gradle fügen Sie in build.gradle hinzu:
testImplementation 'org.testcontainers:testcontainers:1.19.3'\ntestImplementation 'org.testcontainers:postgresql:1.19.3'\n\nBeispiel: Testen eines Repositorys mit PostgreSQL
\nBetrachten wir einen einfachen Dienst zur Benutzerverwaltung. Erstellen wir einen Test, der einen PostgreSQL-Container startet und das Speichern von Daten überprüft.
\n\nimport org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.DynamicPropertyRegistry;\nimport org.springframework.test.context.DynamicPropertySource;\nimport org.testcontainers.containers.PostgreSQLContainer;\nimport org.testcontainers.junit.jupiter.Container;\nimport org.testcontainers.junit.jupiter.Testcontainers;\n\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@Testcontainers\n@SpringBootTest\nclass UserRepositoryTest {\n\n @Container\n static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")\n .withDatabaseName("testdb")\n .withUsername("test")\n .withPassword("test");\n\n @DynamicPropertySource\n static void configureProperties(DynamicPropertyRegistry registry) {\n registry.add("spring.datasource.url", postgres::getJdbcUrl);\n registry.add("spring.datasource.username", postgres::getUsername);\n registry.add("spring.datasource.password", postgres::getPassword);\n }\n\n @Autowired\n private UserRepository userRepository;\n\n @Test\n void shouldSaveAndFindUser() {\n User user = new User("john_doe", "john@example