bigywater.tech
Spring · 83

Testcontainers:用真实的 MySQL 和 Redis 做集成测试

用 H2 做集成测试为什么不放心?Testcontainers 要写多少配置?容器端口怎么办?两个测试类要不要各起一个容器?这篇在本机的 Docker 上起真实的 MySQL 8.4 和 Redis 7,逐项实测。

1 vs 0
同一条 SQL where name = 'ABC':MySQL 命中 1 行,H2 命中 0 行
16949 → 7476 ms
两个测试类各自起一个 MySQL 容器,对比共用一个容器的整条命令中位耗时
2472 ms
Redis 容器的整条测试命令耗时

真实 MySQL

实测 · 本机执行输出
  [T1] 从测试类初始化到测试方法开始(含启动 MySQL 容器、等它就绪、建 Spring 上下文)用了 6874 ms
  [T1] 容器:mysql:8.4,容器 id ec4b8a1472dd,JDBC URL jdbc:mysql://localhost:56365/test
  [T1] docker ps(带 testcontainers 标签):mysql:8.4 0.0.0.0:56365->3306/tcp, [::]:56365->3306/tcp
  [T1] select version() → 8.4.11
  [T1] MySQL:where name = 'ABC' 命中 1 行(存的是 abc)
  [T1] H2 内存库:同样的 SQL 命中 0 行
  [T1] H2:INSERT … ON DUPLICATE KEY UPDATE 失败:Syntax error in SQL statement "insert into u values (1, 2) [*]on duplicate key update v =
  [T1] MySQL:INSERT … ON DUPLICATE KEY UPDATE 之后 v = 2
  退出码 0;成功 1,失败 0;整条命令 11959 ms
  测试结束后带 testcontainers 标签的容器:(没有)

@Container 加 @ServiceConnection 不用写连接信息;端口随机映射;内存库与 MySQL 在大小写比较和 ON DUPLICATE KEY 上行为不同。

Redis 容器

实测 · 本机执行输出
  [T2] 容器:redis:7-alpine,容器 id 7ef4296ea928,映射端口 6379 → 56424
  [T2] set / get → v;ttl 100 秒
  退出码 0;成功 1,失败 0;整条命令 2472 ms
  测试结束后带 testcontainers 标签的容器:(没有)

GenericContainer 加 @ServiceConnection(name = "redis"),起得很快。

共用容器

实测 · 本机执行输出
  各自 @Container(static 字段):
  [P1] 自己的容器 id 93d64966ceb3;select 1 → 1
  [P2] 自己的容器 id 57e1b27d60cc;select 1 → 1
  退出码 0;成功 2,失败 0;整条命令 16949 ms
  各自 @Container(static 字段):3 次整条命令中位 16949 ms(范围 16721~16983)
  共用 @TestConfiguration 里的 @Bean @ServiceConnection 容器(两个类 @Import 同一个配置,共用 Spring 上下文缓存):
  [Q1] 共用的容器 id 487bfe748ed3;select 1 → 1
  [Q2] 共用的容器 id 487bfe748ed3;select 1 → 1
  退出码 0;成功 2,失败 0;整条命令 7476 ms
  共用 @TestConfiguration 里的 @Bean @ServiceConnection 容器(两个类 @Import 同一个配置,共用 Spring 上下文缓存):3 次整条命令中位 7476 ms(范围 7358~7597)
  对比:各自容器 16949 ms,共用容器 7476 ms

把容器放进 @Bean + @ServiceConnection 并在多个测试类里 @Import,可以共用同一个容器,省掉重复启动。

分步演示

Testcontainers
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动(Testcontainers 2.0.5、docker-java 3.7.1、JUnit Jupiter 6.0.3、MySQL 容器 mysql:8.4 的 8.4.11、redis:7-alpine、H2 2.4.240、mysql-connector-j 9.7.0)。Docker 引擎是本机 Docker Desktop 29.8.2,DOCKER_HOST 指向它的 socket;两个镜像事先用本机代理下载后 docker load(所以耗时不含拉镜像);设了 TESTCONTAINERS_RYUK_DISABLED=true 并在每次运行后手动 docker rm 带 testcontainers 标签的容器;耗时是整条 java 命令(含 JVM 与 JUnit 启动),第一、二节单次,第三节 3 次取中位数
    • 没有验证:拉镜像的耗时与镜像缓存策略;Ryuk 清理异常退出的容器;容器复用(.withReuse(true) 与 ~/.testcontainers.properties);@DynamicPropertySource 的旧写法;并行测试;CI 环境(没有本地 Docker 时 DinD、远程 Docker);Kafka、PostgreSQL、MongoDB 等其他容器;Docker Compose 支持(spring-boot-docker-compose);容器启动慢的原因(只测了总时间);MySQL 容器里 8.4.11 与生产版本不同时的差异
    京ICP备2026067502号-1京公网安备11010502063487号