Docker Test Containers in Java Tests – Java测试中的Docker测试容器

最后修改: 2018年 6月 22日

1. Introduction


In this tutorial, we’ll be looking at Java TestContainers library. It allows us to use Docker containers within our tests. As a result, we can write self-contained integration tests that depend on external resources.

在本教程中,我们将了解Java TestContainers库。它允许我们在测试中使用Docker容器。因此,我们可以编写依赖外部资源的独立集成测试。

We can use any resource in our tests that have a docker image. For example, there are images for databases, web browsers, web servers, and message queues. Therefore, we can run them as containers within our tests.


2. Requirements


TestContainers library can be used with Java 8 and higher. Besides, it is compatible with JUnit Rules API.

TestContainers库可用于Java 8及以上版本。此外,它还与JUnit规则API兼容。

First, let’s define the maven dependency for the core functionality:



There are also modules for specialized containers. In this tutorial, we’ll be using PostgreSQL and Selenium. 


Let’s add the relevant dependencies:


    <artifactId>postgresql </artifactId>
    <artifactId>selenium </artifactId>

We can find latest versions on Maven Central.

我们可以在Maven Central上找到最新版本。

Also, we need Docker to run containers. Refer to Docker documentation for installation instructions.


Make sure you’re able to run Docker containers in your test environment.


3. Usage


Let’s configure a generic container rule:


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

We construct a GenericContainer test rule by specifying a docker image name. Then, we configure it with builder methods:


  • We use withExposedPorts to expose a port from the container
  • withCommand defines a container command. It will be executed when the container starts.

The rule is annotated with @ClassRule. As a result, it will start the Docker container before any test in that class runs. The container will be destroyed after all methods are executed.


If you apply @Rule annotation, GenericContainer rule will start a new container for each test method. And it will stop the container when that test method finishes.


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


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

4. Usage Modes


There are several usage modes of the test containers. We saw an example of running a GenericContainer.


TestContainers library has also rule definitions with specialized functionality. They are for containers of common databases like MySQL, PostgreSQL; and others like web clients.


Although we can run them as generic containers, the specializations provide extended convenience methods.


4.1. Databases

4.1. 数据库

Let’s assume we need a database server for data-access-layer integration tests. We can run databases in containers with the help of TestContainers library.


For example, we fire up a PostgreSQL container with PostgreSQLContainer rule. Then, we’re able to use helper methods. These are getJdbcUrl, getUsername, getPassword for database connection:

例如,我们用PostgreSQLContainer规则启动了一个PostgreSQL容器。然后,我们能够使用辅助方法。这些方法是getJdbcUrl, getUsername, getPassword 用于数据库连接:

public PostgreSQLContainer postgresContainer = new PostgreSQLContainer();

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");;
    int result = resultSet.getInt(1);
    assertEquals(1, result);

It is also possible to run PostgreSQL as a generic container. But it’d be more difficult to configure the connection.


4.2. Web Drivers


Another useful scenario is to run containers with web browsers. BrowserWebDriverContainer rule enables running Chrome and Firefox in docker-selenium containers. Then, we manage them with RemoteWebDriver. 


This is very useful for automating UI/Acceptance tests for web applications:


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

4.3. Docker Compose

4.3.Docker Compose

If the tests require more complex services, we can specify them in a docker-compose file:


  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"]

Then, we use DockerComposeContainer rule. This rule will start and run services as defined in the compose file.


We use getServiceHost and getServicePost methods to build connection address to the service:


public static DockerComposeContainer compose = 
  new DockerComposeContainer(
    new File("src/test/resources/test-compose.yml"))
      .withExposedService("simpleWebServer_1", 80);

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. Conclusion


We saw how we could use TestContainers library. It eases developing and running integration tests.


We used GenericContainer rule for containers of given docker images. Then, we looked at PostgreSQLContainer, BrowserWebDriverContainer and DockerComposeContainer rules. They give more functionality for specific use cases.


Finally, code samples here can be found over on GitHub.