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
,极大地降低了集成测试时,依赖组件的部署和维护成本