SpringOne 2022第6个议题《Introduction to Testcontainers》,用3个Demo展示了TestContainers for Java的重要特性,主讲人:Oleg Selajev,辅助人:Cora lberkleid。PS:Oleg Selajev的演讲风格非常有激情。

1.概述
1.1.集成测试对微服务架构的影响深远
演讲者核心观点是:
- 在传统架构下,测试成本的投入情况是**“单元测试">"集成测试">"E2E测试”**(如下图左半部分)。
- 在微服务架构下,存在数量巨大的微服务进程,不太可能全量测试和发布。
- 更合理的做法:仅对本轮迭代有关的微服务进行集成测试,局部发布。
- 如下图右半部分:如果集成测试投入更多,那么与本次发布相关的微服务影响范围是相对可控的,进而E2E测试可能会更简单。
- 总之,集成测试在微服务架构下越来越重要。

1.2.集成测试的新方法
主讲人在这部分展示了没有TestContainers之前、使用TestContainers之后的集成测试手段的差异:
之前:
- 在服务器上部署被测微服务、依赖的组件(如:数据库、Redis、Kafka等)
- 在服务器上以Docker的形式部署被测微服务、依赖的组件
- 在本地电脑上部署被测微服务、依赖的组件
- 对部署成本高的依赖组件,采用模拟的方式
- 如:Wiremock这类Mock Server
- 如:采用h2数据库进行测试,规避安装PostgreSQL这种更重型的数据库

之后:
- STEP1.TestContainers提供了丰富的
Module(这些Module对应各类流行的中间件、数据库等)- 比如:
Redis Module对应Redis - 比如:
Kafka Module对应Kafka
- 比如:
- STEP2.这些
Module,各自都提供了API(这些API封装了Docker API)- 这就意味着我们可以通过某个
Module的API,操纵对应的Docker容器 - 比如:
Redis Module提供1个启动redis容器的API,调用以后,就会在服务器上真的运行起来1个Redis的镜像
- 这就意味着我们可以通过某个
- STEP3.TestContainers再和各类流行的测试框架集成,管理
Module对应的被依赖组件的容器的生命周期- 比如:TestContainers在JUnit的
setUp方法中创建了Redis容器,在TearDown方法中销毁Redis容器
- 比如:TestContainers在JUnit的
- STEP1.TestContainers提供了丰富的

1.3.TestContainers发展历史
演讲者在这一部分讲述了TestContainers创建时的小故事:
2014年,一位名叫Moshe Eshel的大神在GitHub上写了
DockerContainerRule.java这段代码Override了JUnit的
@Before和@After- 在JUnit执行before方法时,会启动指定的Docker容器
- 在JUnit执行after方法时,会关闭指定的Docker容器
2017年,TestContainers完全替代了
DcokerContainerRule.java

听演讲的时候,没太跟上演讲者的语速,和chatGPT印证了一下:
- 的确可以认为:
DcokerContainerRule.java是TestContainers的灵感来源

1.4.TestContainers成熟度
从主讲人表述的信息,TestContainers的确很成熟:
- TestContainers项目在GitHub上是6.6k星
- Docker项目发布的第2年就创建了,是这类测试框架的老前辈
- TestContainers宣称
Works with anything that runs in a Docker container

- 在2022年被ThoughtWorks的咨询报告中,TestContainers被评为
Adopt采纳级

2.Demo0
2.1.TestContainers的测试流程
这是演讲者的第1个Demo:
- 代码解读:
- 在测试类上,增加了
@Testcontainers注解 - 为测试类创建了
GenericContainer对象,此对象表示1个Docker容器 - 在
@Before方法中,调用GenericContainer对象,打印它的Docker容器Id
- 在测试类上,增加了
- **日志解读:**从日志看,TestContainers完整地管理了1个容器的生命周期。
- JUnit先执行
@BeforeEach方法 - JUnit会连接Docker服务器
- Ryuk被启动(Ryuk是啥呢?稍后解释)
- 进行系统检查
- 启动容器镜像
- JUnit先执行

- Ryuk是什么?
- Ruyk是Moby项目中的1个工具
- 这个工具支持对Docker镜像的管理
- chatGPT回答如下:

2.2.如何重用1个容器
- 代码解读:仅需要在测试类创建
GenericContainer对象时,将该对象设置为static - 日志解读:
- 执行测试用例1时,TestContainers创建的Docker容器ID尾号
bc739b5a - 执行测试用例2时,TestContainers创建的Docker容器ID尾号也是
bc739b5a
- 执行测试用例1时,TestContainers创建的Docker容器ID尾号

3.Demo1
3.1.端口配置
- 代码解读:
- 在创建
GenericContainer对象时,通过withExposedPorts方法开放了80端口
- 在创建

- 在测试用例2中,通过
GenericContainer对象的getHost和getFirstMappedPort方法,获得Docker容器的IP和端口
- 在测试用例2中,通过

3.2.日志操作
- 代码解读:
- 获得日志的方式1:调用
GenericContainer对象的getLogs方法

- 获得日志的方式2:使用
Slf4jLogConsumer对象

3.3.使用Dockfile
- 代码解读:
- 创建
GenericContainer对象时,指定DockFile文件路径
- 创建

4.Demo2
4.1.redis+kafka+PostgreSQL
- 代码解读:
- 创建
RedisContainer对象和KafkaContainer对象
- 创建

- 创建
PostgreSQLContainer对象
- 创建

- 设置
Redis容器、PostgreSQL容器的参数:
- 设置

4.2.混沌测试
Chaos Test是主讲人最出彩的一段,通过Toxiproxy,可以很方便地模拟高负载、网络故障等,实战中非常实用。
- 代码解读:
- 创建
Network对象
- 创建

- 创建
Toxiproxy容器对象
- 创建

- 通过
Toxiproxy容器对象,创建redis容器的网络连接代理对象
- 通过

- 设置
redis容器的网络延时为2s,这样,我们就可以模拟生产环境上redis存在2s延时的网络环境了。
- 设置

- chatGPT对Toxiproxy的介绍:

5.小结
通过演讲者的3个Demo,可以得到如下的判断:
TestContainers已经是相当成熟的集成测试组件了
通过TestContainers提供的丰富的
Module,极大地降低了集成测试时,依赖组件的部署和维护成本