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

最后修改: 2018年 6月 22日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

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.

我们可以在测试中使用任何有docker镜像的资源。例如,有数据库、网络浏览器、网络服务器和消息队列的镜像。因此,我们可以在测试中作为容器运行它们。

2. Requirements

2.要求

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:

首先,让我们定义核心功能的maven依赖性。

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>1.11.4</version>
</dependency>

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

还有一些模块用于专门的容器。在本教程中,我们将使用PostgreSQLSelenium。PostgreSQL和Selenium

Let’s add the relevant dependencies:

让我们添加相关的依赖性。

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>postgresql </artifactId>
    <version>1.11.4</version>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>selenium </artifactId>
    <version>1.11.4</version>
</dependency>

We can find latest versions on Maven Central.

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

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

另外,我们需要Docker来运行容器。请参考Docker文档,了解安装说明。

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

确保你能够在你的测试环境中运行Docker容器。

3. Usage

3.使用方法

Let’s configure a generic container rule:

让我们来配置一个通用的容器规则。

@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");

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

我们通过指定一个docker镜像名称来构建一个GenericContainer测试规则。然后,我们用构建器方法对其进行配置。

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

该规则被注解为@ClassRule。因此,它将在该类的任何测试运行之前启动Docker容器该容器将在所有方法执行完毕后被销毁。

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.

如果你应用@Rule注解,GenericContainer规则将为每个测试方法启动一个新容器。当该测试方法完成时,它将停止该容器。

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

我们可以使用IP地址和端口来与容器中运行的进程进行通信

@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. Usage Modes

4.使用模式

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

有几种测试容器的使用模式。我们看到了一个运行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.

TestContainers库也有具有专门功能的规则定义。它们适用于普通数据库的容器,如MySQL、PostgreSQL;以及其他如网络客户端。

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.

让我们假设我们需要一个数据库服务器用于数据访问层的集成测试。在TestContainers库的帮助下,我们可以在容器中运行数据库。

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 用于数据库连接:

@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);
}

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

也可以把PostgreSQL作为一个通用容器来运行。但是配置连接会比较困难。

4.2. Web Drivers

4.2.网络驱动程序

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. 

另一个有用的场景是使用网络浏览器来运行容器。BrowserWebDriverContainer规则使ChromeFirefoxdocker-selenium容器中运行。然后,我们用RemoteWebDriver来管理它们。

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

这对网络应用的UI/验收测试的自动化非常有用:

@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

4.3.Docker Compose

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

如果测试需要更复杂的服务,我们可以在docker-compose文件中指定它们。

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

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

然后,我们使用DockerComposeContainer规则。这个规则将启动和运行编译文件中定义的服务。

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

我们使用getServiceHostgetServicePost方法来建立服务的连接地址:

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

5.总结

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

我们看到我们如何使用TestContainers库。它简化了开发和运行集成测试。

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.

我们使用GenericContainer规则,用于给定docker镜像的容器。然后,我们查看了PostgreSQLContainer、BrowserWebDriverContainerDockerComposeContainer规则。它们为特定的用例提供了更多的功能。

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

最后,这里的代码样本可以在GitHub上找到over