Docker-Testcontainer in Java-Tests

1. Einleitung

In diesem Tutorial sehen wir uns die Java TestContainers- Bibliothek an. Es ermöglicht uns, Docker-Container in unseren Tests zu verwenden. Infolgedessen können wir in sich geschlossene Integrationstests schreiben, die von externen Ressourcen abhängen.

Wir können jede Ressource in unseren Tests verwenden, die ein Docker-Image haben. Beispielsweise gibt es Bilder für Datenbanken, Webbrowser, Webserver und Nachrichtenwarteschlangen. Daher können wir sie in unseren Tests als Container ausführen.

2. Anforderungen

Die TestContainers- Bibliothek kann mit Java 8 und höher verwendet werden. Außerdem ist es mit der JUnit Rules API kompatibel.

Definieren wir zunächst die Maven-Abhängigkeit für die Kernfunktionalität:

 org.testcontainers testcontainers 1.11.4 

Es gibt auch Module für Spezialcontainer. In diesem Tutorial verwenden wir PostgreSQL und Selenium.

Fügen wir die relevanten Abhängigkeiten hinzu:

 org.testcontainers postgresql  1.11.4   org.testcontainers selenium  1.11.4 

Wir können die neuesten Versionen auf Maven Central finden.

Außerdem benötigen wir Docker, um Container auszuführen . Installationsanweisungen finden Sie in der Docker-Dokumentation.

Stellen Sie sicher, dass Sie Docker-Container in Ihrer Testumgebung ausführen können.

3. Verwendung

Konfigurieren wir eine generische Containerregel:

@ClassRule public static GenericContainer simpleWebServer = new GenericContainer("alpine:3.2") .withExposedPorts(80) .withCommand("/bin/sh", "-c", "while true; do echo " + "\"HTTP/1.1 200 OK\n\nHello World!\" | nc -l -p 80; done");

Wir erstellen eine GenericContainer-Testregel , indem wir einen Docker-Image-Namen angeben . Dann konfigurieren wir es mit Builder-Methoden:

  • Wir verwenden withExposedPorts , um einen Port aus dem Container verfügbar zu machen
  • withCommand definiert einen Containerbefehl. Es wird ausgeführt, wenn der Container startet.

Die Regel wird mit @ClassRule kommentiert . Infolgedessen wird der Docker-Container gestartet, bevor ein Test in dieser Klasse ausgeführt wird . Der Container wird zerstört, nachdem alle Methoden ausgeführt wurden.

Wenn Sie die Annotation @Rule anwenden, startet die GenericContainer- Regel für jede Testmethode einen neuen Container. Und es stoppt den Container, wenn diese Testmethode abgeschlossen ist.

Wir können IP-Adresse und Port verwenden, um mit dem im Container ausgeführten Prozess zu kommunizieren :

@Test public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse() throws Exception { String address = "//" + simpleWebServer.getContainerIpAddress() + ":" + simpleWebServer.getMappedPort(80); String response = simpleGetRequest(address); assertEquals(response, "Hello World!"); }

4. Nutzungsmodi

Es gibt verschiedene Verwendungsmodi der Testcontainer. Wir haben ein Beispiel für die Ausführung eines GenericContainer gesehen.

Die TestContainers- Bibliothek verfügt auch über Regeldefinitionen mit speziellen Funktionen. Sie sind für Container gängiger Datenbanken wie MySQL, PostgreSQL; und andere mögen Web-Clients.

Obwohl wir sie als generische Container ausführen können, bieten die Spezialisierungen erweiterte Komfortmethoden.

4.1. Datenbanken

Nehmen wir an, wir benötigen einen Datenbankserver für Integrationstests auf Datenzugriffsebene. Mit Hilfe der TestContainers-Bibliothek können wir Datenbanken in Containern ausführen.

Zum Beispiel starten wir einen PostgreSQL-Container mit der PostgreSQLContainer- Regel. Dann können wir Hilfsmethoden verwenden. Dies sind getJdbcUrl, getUsername, getPassword für die Datenbankverbindung:

@Rule public PostgreSQLContainer postgresContainer = new PostgreSQLContainer(); @Test public void whenSelectQueryExecuted_thenResulstsReturned() throws Exception { String jdbcUrl = postgresContainer.getJdbcUrl(); String username = postgresContainer.getUsername(); String password = postgresContainer.getPassword(); Connection conn = DriverManager .getConnection(jdbcUrl, username, password); ResultSet resultSet = conn.createStatement().executeQuery("SELECT 1"); resultSet.next(); int result = resultSet.getInt(1); assertEquals(1, result); }

Es ist auch möglich, PostgreSQL als generischen Container auszuführen. Es wäre jedoch schwieriger, die Verbindung zu konfigurieren.

4.2. Web-Treiber

Ein weiteres nützliches Szenario ist das Ausführen von Containern mit Webbrowsern. Die BrowserWebDriverContainer- Regel ermöglicht das Ausführen von Chrome und Firefox in Docker-Selen- Containern. Dann verwalten wir sie mit RemoteWebDriver.

Dies ist sehr nützlich für die Automatisierung von UI- / Abnahmetests für Webanwendungen:

@Rule public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer() .withCapabilities(new ChromeOptions()); @Test public void whenNavigatedToPage_thenHeadingIsInThePage() { RemoteWebDriver driver = chrome.getWebDriver(); driver.get("//example.com"); String heading = driver.findElement(By.xpath("/html/body/div/h1")) .getText(); assertEquals("Example Domain", heading); }

4.3. Docker Compose

Wenn die Tests komplexere Dienste erfordern, können wir sie in einer Docker-Compose- Datei angeben :

simpleWebServer: image: alpine:3.2 command: ["/bin/sh", "-c", "while true; do echo 'HTTP/1.1 200 OK\n\nHello World!' | nc -l -p 80; done"]

Dann verwenden wir die DockerComposeContainer- Regel. Diese Regel startet und führt Dienste aus, wie in der Erstellungsdatei definiert.

Wir verwenden die Methoden getServiceHost und getServicePost , um die Verbindungsadresse zum Dienst zu erstellen:

@ClassRule public static DockerComposeContainer compose = new DockerComposeContainer( new File("src/test/resources/test-compose.yml")) .withExposedService("simpleWebServer_1", 80); @Test public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse() throws Exception { String address = "//" + compose.getServiceHost("simpleWebServer_1", 80) + ":" + compose.getServicePort("simpleWebServer_1", 80); String response = simpleGetRequest(address); assertEquals(response, "Hello World"); }

5. Schlussfolgerung

Wir haben gesehen, wie wir die TestContainers- Bibliothek verwenden können. Es erleichtert die Entwicklung und Ausführung von Integrationstests.

Wir haben die GenericContainer- Regel für Container mit bestimmten Docker-Bildern verwendet. Dann haben wir uns die Regeln für PostgreSQLContainer, BrowserWebDriverContainer und DockerComposeContainer angesehen . Sie bieten mehr Funktionen für bestimmte Anwendungsfälle.

Schließlich finden Sie Codebeispiele hier auf GitHub.