Java测试中的Docker测试容器

2023/05/09

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.16.3</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. 用法

让我们配置一个通用的容器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");

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

  • 我们使用withExposedPorts从容器中公开一个端口
  • withCommand定义一个容器命令,它将在容器启动时执行

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

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

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

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

4. 使用方式

测试容器有多种使用模式,我们在上面演示了一个运行GenericContainer的示例。

TestContainers库还具有带有专用功能的Rule定义。它们用于MySQL、PostgreSQL等常见数据库的容器;以及其他一些如Web客户端的容器。

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

4.1 数据库

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

例如,我们使用PostgreSQLContainer Rule启动一个PostgreSQL容器。然后,我们可以使用工具方法。这些是用于数据库连接的getJdbcUrl、getUsername、getPassword

@Testable
public class PostgreSqlContainerLiveTest {
    @Rule
    public PostgreSQLContainer postgresContainer = new PostgreSQLContainer();

    @Test
    public void whenSelectQueryExecuted_thenResultsReturned() throws Exception {
        ResultSet resultSet = performQuery(postgresContainer, "SELECT 1");
        resultSet.next();
        int result = resultSet.getInt(1);

        assertEquals(1, result);
    }

    private ResultSet performQuery(PostgreSQLContainer postgres, String query) throws SQLException {
        String jdbcUrl = postgres.getJdbcUrl();
        String username = postgres.getUsername();
        String password = postgres.getPassword();
        Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
        return conn.createStatement()
              .executeQuery(query);
    }
}

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

4.2 WebDriver

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

这对于自动化Web应用程序的UI/验收测试非常有用

public class WebDriverContainerLiveTest {
    @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

如果测试需要更复杂的服务,我们可以在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 Rule。此Rule将启动并运行compose文件中定义的服务。

我们使用getServiceHost和getServicePost方法来构建与服务的连接地址

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

    @Test
    public void givenSimpleWebServerContainer_whenGetRequest_thenReturnsResponse() throws Exception {
        String address = "http://" + compose.getServiceHost("simpleWebServer_1", 80)
              + ":" + compose.getServicePort("simpleWebServer_1", 80);
        String response = simpleGetRequest(address);

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

    // simpleGetRequest method ...
}

5. 总结

我们介绍了如何使用TestContainers库来简化开发和运行集成测试。

我们对给定Docker镜像的容器使用了GenericContainer Rule。然后,我们介绍了PostgreSQLContainer、BrowserWebDriverContainer和DockerComposeContainerRule,它们为特定用例提供了更多功能。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章