商城首页欢迎来到中国正版软件门户

您的位置:首页 >SpringBoot整合Testcontainers实现数据库与中间件集成测试

SpringBoot整合Testcontainers实现数据库与中间件集成测试

  发布于2026-07-30 阅读(0)

扫一扫,手机访问

单元测试可以 Mock 掉数据库,但集成测试需要真实环境。Testcontainers 在测试时启动 Docker 容器,用完后自动销毁。

SpringBoot整合Testcontainers实现数据库与中间件集成测试

说实话,单元测试里用 Mock 爽是爽,但一到集成测试,假数据就露馅了。真刀真枪地测数据库、测缓存,就得有真实环境。过去我们得自己搭测试数据库、手动清理数据,麻烦不说,还容易污染环境。Testcontainers 的出现,算是把这个问题从根上解决了——它直接在测试时拉起 Docker 容器,跑完自动销毁,干干净净,不留痕迹。

下面直接上干货,看看怎么在 Spring Boot 项目里把它用起来。

一、引入依赖

先用 Ma ven 把 Testcontainers 的核心包和 MySQL 模块加进来:


    org.testcontainers
    testcontainers
    1.19.3
    test


    org.testcontainers
    mysql
    1.19.3
    test

版本号建议统一,这里用的是 1.19.3,实际使用时可以换成最新的稳定版。

二、MySQL 集成测试

最经典的场景——测试数据库操作。写一个用户仓库的测试类,用 @Testcontainers 注解标记,然后声明一个静态的 MySQL 容器:

@SpringBootTest
@Testcontainers
class UserRepositoryTest {

    @Container
    static MySQLContainer mysql = new MySQLContainer<>("mysql:8.0")
        .withDatabaseName("testdb")
        .withUsername("test")
        .withPassword("test");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", mysql::getJdbcUrl);
        registry.add("spring.datasource.username", mysql::getUsername);
        registry.add("spring.datasource.password", mysql::getPassword);
    }

    @Autowired
    private UserRepository userRepository;

    @Test
    void testSa veAndFind() {
        User user = new User();
        user.setName("张三");
        user.setEmail("zhangsan@test.com");
        userRepository.sa ve(user);
        assertThat(user.getId()).isNotNull();
        User found = userRepository.findById(user.getId()).orElse(null);
        assertThat(found).isNotNull();
        assertThat(found.getName()).isEqualTo("张三");
    }
}

关键点在于 @DynamicPropertySource——它把容器启动后的 JDBC 地址、用户名、密码动态注入到 Spring 的配置中,这样测试就能连上临时数据库了。测试方法里就是常规的增删查改,完全不需要关心数据库在哪里。

三、Redis 集成测试

缓存测试同理,再加一个 Redis 的依赖:


    org.testcontainers
    redis
    1.19.3
    test

然后写个测试类,用 GenericContainer 启动 Redis 7 镜像,暴露 6379 端口:

@SpringBootTest
@Testcontainers
class RedisCacheTest {

    @Container
    static GenericContainer redis = new GenericContainer<>(
        DockerImageName.parse("redis:7-alpine"))
        .withExposedPorts(6379);

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.redis.host", redis::getHost);
        registry.add("spring.redis.port", () -> redis.getMappedPort(6379));
    }

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Test
    void testSetAndGet() {
        redisTemplate.opsForValue().set("test:key", "hello");
        String value = redisTemplate.opsForValue().get("test:key");
        assertThat(value).isEqualTo("hello");
    }
}

注意这里用的是 GenericContainer,因为它没有专用的 Redis 模块(其实有,但用通用容器也能搞定)。同样动态注入 host 和 port,测试里直接操作 RedisTemplate,和真实环境没区别。

四、共享容器

如果项目里既需要 MySQL 又需要 Redis,每个测试类都单独启动容器会很慢。一个常见的做法是抽一个抽象基类,把容器声明成静态的,让所有集成测试继承它:

@Testcontainers
abstract class AbstractIntegrationTest {

    @Container
    static MySQLContainer mysql = new MySQLContainer<>("mysql:8.0");

    @Container
    static GenericContainer redis = new GenericContainer<>(
        DockerImageName.parse("redis:7-alpine"))
        .withExposedPorts(6379);

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", mysql::getJdbcUrl);
        registry.add("spring.datasource.username", mysql::getUsername);
        registry.add("spring.datasource.password", mysql::getPassword);
        registry.add("spring.redis.host", redis::getHost);
        registry.add("spring.redis.port", () -> redis.getMappedPort(6379));
    }
}

这样一来,所有子类测试都能复用同一个 MySQL 和 Redis 容器,容器在整个测试套件中只会启动一次,大大节省时间。而且容器用完后自动销毁,不会残留任何数据——这才是集成测试该有的样子。

本文转载于:https://www.jb51.net/program/368309km9.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注