https://testcontainers.com/getting-started/

什么是 Testcontainers?

Testcontainers 是一个库,它提供简单轻量级的 API,用于引导本地开发和测试依赖项与包装在 Docker 容器中的真实服务。使用 Testcontainers,您可以编写依赖于生产中使用的相同服务的测试,而无需模拟或内存服务。

Testcontainers 解决了哪些问题?

云原生基础设施和微服务已经将控制权从开发人员手中夺走,使得在本地工作变得非常困难。假设您是一名开发人员,在以下架构中开发“我的服务”:

虽然您只拥有 My Service 及其数据存储(绿色),但您拥有本地开发和集成测试所需的多个下游依赖项(蓝色)。这带来了以下挑战:

  • 在运行测试之前,您必须确保基础设施已启动并正在运行,且已预先配置为所需状态。
  • 如果资源(数据库、消息代理等)在多个用户或 CI 管道之间共享,则测试结果是不确定的,因为存在数据损坏和配置漂移的可能性。

规避这些挑战的一种方法是依赖内存数据库、嵌入式服务、模拟和生产依赖项的其他虚假副本。但是,这些方法也带来了自身的问题(例如,内存服务可能不具备生产服务的所有功能,并且行为略有不同)。

Testcontainers 通过在 Docker 容器中运行应用程序依赖项(例如数据库、消息代理等)来解决这些问题,并通过与那些真实服务对话并为您的测试代码提供编程 API 来帮助执行可靠且可重复的测试。

在前面的例子中,你可以自由地开发和测试“我的服务”的真实依赖关系,通过直接从代码中使用 Testcontainers 进行配置:

使用 Testcontainers 的好处:

  • 按需独立基础设施配置: 您无需预先配置集成测试基础设施。测试容器将在运行测试之前提供所需的服务。即使多个构建管道并行运行,也不会产生测试数据污染,因为每个管道都使用一组独立的服务运行。

  • 在本地和 CI 环境中获得一致的体验: 您可以直接从 IDE 运行集成测试,就像运行单元测试一样。无需推送更改并等待 CI 管道完成。

  • 使用等待策略进行可靠的测试设置: 在测试中使用 Docker 容器之前,需要启动并完全初始化它们。Testcontainers 库提供了几种现成的等待策略实现,以确保容器(以及其中的应用程序)完全初始化。Testcontainers 模块已经为给定技术实现了相关的等待策略,您可以随时实现自己的策略或根据需要创建复合策略。

  • 高级网络功能: Testcontainers 库将容器的端口映射到主机上可用的随机端口,以便您的测试可靠地连接到这些服务。您甚至可以创建一个 (Docker) 网络并将多个容器连接在一起,以便它们通过静态 Docker 网络别名相互通信。

  • 自动清理: Testcontainers 库负责在测试执行完成后使用 Ryuk sidecar 容器自动删除任何已创建的资源(容器、卷、网络等)。在启动所需容器时,Testcontainers 会将一组标签附加到已创建的资源(容器、卷、网络等),然后 Ryuk 通过匹配这些标签自动执行资源清理。即使测试过程异常退出(例如发送 SIGKILL),此方法也能可靠地工作。

Docker 和 Docker Compose 的区别

Docker 和 Docker Compose 也可以直接用于启动测试所需的依赖项,但这种方法有缺点。使用原始 Docker 命令或使用 Docker Compose 创建可靠且完全初始化的服务依赖项需要熟悉 Docker 内部结构以及如何在容器中最佳地运行特定技术。例如,直接使用 Docker 命令或 docker-compose 创建动态“集成测试环境”可能会导致端口冲突、容器未完全初始化或在测试开始时无法进行交互等。Testcontainers 库充分利用了 Docker 容器的全部功能,并通过惯用的 API 将它们暴露给开发人员。

支持的语言和先决条件

Testcontainers 为最流行的语言和平台提供支持,包括 Java、.NET、Go、NodeJS、Python、Rust 和 Haskell。

要运行基于 Testcontainers 的测试,您需要一个兼容 Docker-API 的容器运行时,例如使用Testcontainers Cloud或在本地安装 Docker。官方支持以下容器运行时环境:

Docker 桌面
Linux 上的 Docker 引擎
测试容器云

TestContainers 工作流程

您可以将 Testcontainers 与任何您熟悉的测试库一起使用。典型的基于 Testcontainers 的集成测试的工作原理如下:

  • 测试执行前: 使用 Testcontainers API 将所需服务(数据库、消息系统等)作为 Docker 容器启动。所需容器启动后,配置或更新应用程序配置以使用这些容器化服务,并可选择初始化测试所需的数据。

  • 在测试执行期间:您的测试使用这些容器化的服务运行。

  • 测试执行后: 无论测试是否成功执行或出现任何失败,Testcontainers 都会负责销毁容器。

我可以使用哪些技术进行测试?

您可以使用 Testcontainers 对任何 Dockerized 软件运行测试。Testcontainers 可以从任何现有 Docker 映像、Dockerfile 甚至 Docker Compose 文件启动容器。

GenericContainer 抽象

Testcontainers 提供了一个名为 GenericContainer 的编程抽象,它代表 Docker 容器。您可以使用 GenericContainer 启动 Docker 容器,获取任何容器信息(例如主机名(可访问映射端口的主机)、映射端口)并停止容器。

例如,您可以按如下方式使用Testcontainers for Java中的GenericContainer :

GenericContainer container = new GenericContainer("postgres:15")
        .withExposedPorts(5432)
        .waitingFor(new LogMessageWaitStrategy()
            .withRegEx(".*database system is ready to accept connections.*\\s")
            .withTimes(2)
            .withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS)));
container.start();
var username = "test";
var password = "test";
var jdbcUrl = "jdbc:postgresql://" + container.getHost() + ":" + container.getMappedPort(5432) + "/test";
//perform db operations
container.stop();

GenericContainer API 也适用于其他受支持的语言。

测试容器模块

Testcontainers 为各种常用基础设施依赖项提供了模块,包括关系数据库、NoSQL 数据存储、搜索引擎、消息代理等。完整列表请参阅https://testcontainers.com/modules/

特定于技术的模块是 GenericContainer 之上的更高级别的抽象,有助于配置和运行这些技术而无需任何样板,并可以轻松访问其相关参数。

例如,您可以使用 Testcontainers postgres 模块的 PostgreSQLContainer 代替 GenericContainer,如下所示:

PostgreSQLContainer postgres = new PostgreSQLContainer("postgres:15");
postgres.start();
var username = postgres.getUsername();
var password = postgres.getPassword();
var jdbcUrl = postgres.getJdbcUrl();
//perform db operations
postgres.stop();

使用模块可以通过提供方便的辅助方法(例如getJdbcUrl() )使与技术的交互变得更加简单。Testcontainers 模块还负责实现特定于技术的端口映射、适当的等待策略等。

作者:Jeebiz  创建时间:2024-08-09 23:42
最后编辑:Jeebiz  更新时间:2024-08-09 23:57