Contents

TestContainers库简介

1. 简介

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

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

2. 要求

TestContainers库可用于 Java 8 及更高版本。此外,它与 JUnit Rules API 兼容。

首先,让我们为核心功能定义 maven 依赖项:

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

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

让我们添加相关的依赖项:

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

我们可以在 Maven Central 上找到最新版本。 此外,我们需要 Docker 来运行容器。有关安装说明,请参阅Docker 文档 。 确保您能够在测试环境中运行 Docker 容器。

3. 用法

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

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

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

  • 我们使用 withExposedPorts从容器中暴露一个端口
  • withCommand定义了一个容器命令。它将在容器启动时执行。

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

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

我们可以使用 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. 使用方式

测试容器有多种使用模式。我们看到了一个运行 GenericContainer 的示例。

TestContainers库还具有具有专门功能的规则定义。它们用于 MySQL、PostgreSQL 等常见数据库的容器;和其他人喜欢网络客户端。

尽管我们可以将它们作为通用容器运行,但特化提供了扩展的便利方法。

4.1. 数据库

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

例如,我们使用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);
}

也可以将 PostgreSQL 作为通用容器运行。但是配置连接会更加困难。

4.2. 网络驱动程序

另一个有用的场景是使用 Web 浏览器运行容器。 BrowserWebDriverContainer规则允许在 docker -selenium容器中运行Chrome和 Firefox  。然后,我们使用RemoteWebDriver 管理它们。

这对于自动化 Web 应用程序的 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文件中指定它们 :

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

然后,我们使用DockerComposeContainer规则。此规则将启动并运行 compose 文件中定义的服务。

我们使用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");
}