测试 Elasticsearch,现在变得更简单了

作者:来自 Elastic Piotr Przybyl

解释由于 Elasticsearch 9.x、现代 Java 客户端以及 Testcontainers 2.x 的改进,Elasticsearch 集成测试如何变得更加简单。

上手体验 Elasticsearch:深入了解我们在 Elasticsearch Labs 仓库中的示例 notebooks,开始免费的云端试用,或者现在就在本地机器上尝试 Elastic。


当我最初写关于使用 Testcontainers for Java 测试 Elasticsearch 时,重点非常务实:如果你关心正确性,就应该针对真实节点进行测试;如果你关心信心,你的集成测试应该尽可能接近生产环境;如果你关心可维护性,你的设置不应该变成一个由 mocks 和假设组成的迷宫。

这一理念并没有改变。

然而改变的是,现在实现这一目标所需的努力已经大大减少。借助 Elasticsearch 9.x、现代 Java 客户端以及 Testcontainers 2.x,编写集成测试的体验明显更加顺畅,就像一层附带的复杂性被悄然移除了。

本文附带的示例刻意保持简单,可以在这里找到。

它并不试图展示复杂的索引策略或精细的数据管道;相反,它专注于基础,因为正是在这些基础之处,改进最为明显。

当工具不再成为阻碍时

任何维护过几年测试套件的人都会认出这种模式:你引入一个新库,一个传递依赖引入了一些意想不到的东西,不久之后,你就在不同测试引擎版本之间周旋,而不是在编写测试。

在 Testcontainers 2.x 中,这种周旋基本消失了。依赖结构更加清晰,模块更加明确,与旧测试框架的意外耦合不再悄悄出现。从实际角度来看,在测试中添加 Elasticsearch 支持现在就像声明以下内容一样简单:

复制代码
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>testcontainers-elasticsearch</artifactId>
  <version>2.0.3</version>
  <scope>test</scope>
</dependency>

并且,如果你正在使用 JUnit Jupiter 集成:

复制代码
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>testcontainers-junit-jupiter</artifactId>
  <version>2.0.3</version>
  <scope>test</scope>
</dependency>

不需要添加各种排除项,不需要屏蔽旧版引擎,也不会再有那种担心在下次升级时某些隐藏问题突然浮现的不安感。配置变得几乎平淡无奇,而在构建工具的语境中,这反而是一种优点。

一个具有完整安全性的真实 Elasticsearch 节点

在演示测试中,我们使用官方的 Elasticsearch 9.3.1 Docker 镜像:

复制代码
var container =
    new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:9.3.1");

container.start();

乍一看,这可能与较旧的示例类似,但细微的差别在于我们不再需要做的事情。我们不再禁用安全性,不再绕过 SSL,也不再为了让测试更方便而简化环境。

相反,一旦容器启动,我们就构建一个使用 REST API 并进行正确认证的客户端:

复制代码
try (var client = ElasticsearchClient.of(c -> c
     .host("https://" + container.getHttpHostAddress())
     .usernameAndPassword("elastic", ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)
     .sslContext(container.createSslContextFromCa())
)) {

这里特别值得一提的是客户端构建本身已经变得多么简洁。在早期版本中,创建一个 Elasticsearch 客户端通常意味着需要处理多个中间对象、显式配置传输层、封装底层客户端,并为这些本质上只是"管道工作"的内容编写一定量的代码。而现在,信噪比显著提高。builder 封装了必要的细节,容器提供客户端所需的一切,最终配置可以用几行清晰易读的代码完成。

同样重要的是,ElasticsearchClient 是 AutoCloseable,这意味着它可以自然地与 try-with-resources 集成,在无需额外繁琐操作的情况下确保正确清理。其生命周期是显式的、简洁的且自包含的,这正是集成测试所需要的------关注行为,而不是基础设施管理。

容器暴露了构建合法且安全连接所需的一切,而客户端可以自然地与之集成,这意味着测试环境在所有关键方面都与生产环境保持一致,同时不会给开发者带来额外的认知负担。

这种在真实性与简洁性之间的对齐,也许是最有意义的改进之一。

类型化 API 改变了测试的特性

Elasticsearch Java 客户端的演进也重塑了集成测试的阅读和体验方式。过去的方法通常需要解析 JSON 响应或处理松散类型结构,而现代客户端提供了基于 builder 的强类型 API,在编译期就能引导你构建合法的请求结构。

在示例中,我们执行了一个简单的集群健康检查:

复制代码
var health = client.cluster().health();

Assertions.assertEquals("docker-cluster", health.clusterName());
Assertions.assertEquals(HealthStatus.Green, health.status());

这里引人注目的不是操作的复杂性,而是几乎没有阻力。不需要从 maps 中手动提取数据,不需要基于未类型化字符串值来编写断言,也不需要绕到低层响应处理。测试代码看起来与应用代码几乎没有区别,这在某种程度上强化了这样一个观点:集成测试并不是一类拥有不同规则的特殊代码,而只是同一 API 的另一种使用者。

当生产代码与测试代码之间的边界变得更模糊时,信心几乎会自然而然地提升。

将测试当作一个故事来阅读

如果你查看完整的测试用例:

复制代码
@Test
void newClientTest() throws IOException {
    try (var container =
             new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:9.3.1")) {
        
        container.start();
        
        try (
            var client = ElasticsearchClient.of(c ->
                c.host("https://" + container.getHttpHostAddress())
                    .usernameAndPassword("elastic", ElasticsearchContainer.ELASTICSEARCH_DEFAULT_PASSWORD)
                    .sslContext(container.createSslContextFromCa()))) {

            HealthResponse health = client.cluster().health();

            Assertions.assertEquals("docker-cluster", health.clusterName());
            Assertions.assertEquals(HealthStatus.Green, health.status());
        }
    }
}

你会注意到,它读起来不像一个配置脚本,而更像一个简短的叙述:

  • 我们定义容器。
  • 我们启动容器。
  • 我们构建客户端。
  • 我们调用真实 API。
  • 我们断言结果。

支撑性的基础设施逐渐淡出背景,使测试的意图清晰可见。这种清晰性并非偶然,而是 Testcontainers 和 Elasticsearch 客户端一系列渐进改进的累积效果。

高级模式仍然适用

在之前的文章《使用真实 Elasticsearch 的更快集成测试》和《使用真实 Elasticsearch 的高级集成测试》中讨论的更高级技术并没有过时。复用容器以加速大型测试套件、自定义集群设置、预加载索引或测试基于角色的访问场景,仍然完全有效,并且在许多情况下是必不可少的。

改进之处在于基础体验。最简单的集成测试------仅需要一个真实节点和一个真实客户端------不再需要防御性配置或依赖折腾。它默认就是简洁、表达力强且接近生产环境的。

无声的进步

生态系统并没有发生戏剧性的重写,也没有迫使人们重新思考一切的破坏性迁移指南。相反,是 API 和依赖的持续优化,每个版本都在这里抹平一个棱角,在那里消除一个意外。

结果并不浮夸,但却切实可感。现在为 Elasticsearch 编写集成测试,更像是在一个缩小版的真实系统上进行操作,而不是在组装一个测试工具链。

有时候,进步会高调地宣告自己。有时候,它悄然到来,以代码更易读、解释更少的形式出现。在这个案例中,属于后者,而对于那些关注干净、可靠集成测试的人来说,这已经足够了。

那么,如果我们也能对 Kibana 做类似的事情会怎样?听起来很有吸引力吗?敬请期待!

原文:https://www.elastic.co/search-labs/blog/elasticsearch-integration-tests

相关推荐
Elasticsearch17 小时前
如何通过 Claude Code 来写入 CSV 数据到 Elasticsearch
elasticsearch
得物技术2 天前
从埋点需求到规则资产:Hermes Agent 重构得物数仓工作流
大数据·llm·ai编程
久美子2 天前
AI驱动数仓建设的Harness工程实践——本体建模、知识分层与上下文工程
大数据
大树883 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
大志哥1233 天前
ES和Logstash日志链路系统上线后遭遇切片爆炸(解决)
大数据·elasticsearch
霸道流氓气质3 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工3 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
果丁智能3 天前
物联网智能锁赋能集中式住宿:身份核验与远程权限管控的全链路技术实践
大数据·人工智能·物联网·智能家居
酣大智3 天前
ARP代理--工作原理
运维·网络·arp·arp代理