Docker Test Container in Java Tests

Docker-Testcontainer in Java-Tests

1. Einführung

In this tutorial, we’ll be looking at Java TestContainers library. Damit können wir Docker-Container in unseren Tests verwenden. Als Ergebnis können wir unabhängige Integrationstests schreiben, die von externen Ressourcen abhängen.

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

2. Bedarf

Die Bibliothek vonTestContainerskann 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 wirPostgreSQL andSelenium. 

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 aufMaven Central finden.

Also, we need Docker to run containers. Installationsanweisungen finden Sie unterDocker documentation.

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

3. Verwendungszweck

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 eineGenericContainer-Testregel, indem wir einen Docker-Image-Namen angeben. Dann konfigurieren wir es mit Builder-Methoden:

  • Wir verwendenwithExposedPorts , um einen Port aus dem Container freizulegen

  • withCommand definiert einen Containerbefehl. Es wird ausgeführt, wenn der Container startet.

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

Wenn Sie die Anmerkung von@Ruleanwenden, startet die Regel vonGenericContainerfür jede Testmethode einen neuen Container. Und es stoppt den Container, wenn diese Testmethode beendet ist.

We can use IP address and port to communicate with the process running in the container:

@Test
public void givenSimpleWebServerContainer_whenGetReuqest_thenReturnsResponse()
  throws Exception {
    String address = "http://"
      + simpleWebServer.getContainerIpAddress()
      + ":" + simpleWebServer.getMappedPort(80);
    String response = simpleGetRequest(address);

    assertEquals(response, "Hello World!");
}

4. Nutzungsmodi

Es gibt mehrereusage modes der Testbehälter. Wir haben ein Beispiel für die Ausführung vonGenericContainer. gesehen

Die Bibliothek vonTestContainersverfü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 derPostgreSQLContainer-Regel. Dann können wir Hilfsmethoden verwenden. These are getJdbcUrl, getUsername, getPassword for database connection:

@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 ist jedoch schwieriger, die Verbindung zu konfigurieren.

4.2. Web-Treiber

Ein weiteres nützliches Szenario ist das Ausführen von Containern mit Webbrowsern. BrowserWebDriverContainer rule ermöglicht das Ausführen vonChrome andFirefox indocker-selenium containern. Dann verwalten wir sie mitRemoteWebDriver. 

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("http://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 einerdocker-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 dieDockerComposeContainer-Regel. Diese Regel startet und führt Dienste aus, wie in der Erstellungsdatei definiert.

Wir verwenden die MethodengetServiceHost undgetServicePost, um die Verbindungsadresse zum Dienst aufzubauen:

@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 = "http://" + compose.getServiceHost("simpleWebServer_1", 80) + ":" + compose.getServicePort("simpleWebServer_1", 80);
    String response = simpleGetRequest(address);

    assertEquals(response, "Hello World");
}

5. Fazit

Wir haben gesehen, wie wir die Bibliothek vonTestContainersnutzen können. Es vereinfacht die Entwicklung und Ausführung von Integrationstests.

Wir habenGenericContainer rule für Container mit bestimmten Docker-Bildern verwendet. Dann haben wir uns die Regeln vonPostgreSQLContainer, BrowserWebDriverContainer undDockerComposeContainerangesehen. Sie bieten mehr Funktionen für bestimmte Anwendungsfälle.

Schließlich können Codebeispiele hierover on GitHub gefunden werden.