Testcontainers 系列专题:第 2 篇 核心功能与基本用法

Testcontainers 系列专题 - 第 2 篇:核心功能与基本用法

引言

在第 1 篇中,我们了解了 Testcontainers 的基本概念,并通过一个简单的 PostgreSQL 示例快速上手。本篇将进一步探索 Testcontainers 的核心功能,包括常用容器类、容器配置、生命周期管理,以及如何与主流测试框架集成。通过这些内容,你将能够更自信地使用 Testcontainers 来编写可靠的测试。


核心功能概览

Testcontainers 提供了丰富的功能,让开发者可以轻松管理测试中的容器化依赖。以下是本篇将重点介绍的几个方面:

  1. 常用容器类:开箱即用的专用容器支持。
  2. 容器配置:自定义端口、环境变量等。
  3. 生命周期管理:控制容器的启动、停止和重用。
  4. 测试框架集成:与 JUnit 和 Spring Boot 无缝协作。

常用容器类

Testcontainers 提供了多种预配置的容器类,针对常见服务进行了封装,简化使用过程。以下是几个典型例子:

  • GenericContainer:通用容器类,可运行任何 Docker 镜像。
  • PostgreSQLContainer:专为 PostgreSQL 设计的容器,内置 JDBC 支持。
  • MySQLContainer:用于 MySQL 数据库的容器。
  • KafkaContainer:快速启动 Apache Kafka。
  • LocalStackContainer:模拟 AWS 服务(如 S3、SQS)。

示例:使用 MySQLContainer

以下是一个使用 MySQLContainer 的简单测试:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

import static org.junit.jupiter.api.Assertions.assertTrue;

@Testcontainers
public class SimpleMySQLTest {

    @Container
    public MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
            .withDatabaseName("testdb")
            .withUsername("user")
            .withPassword("password");

    @Test
    public void testCreateTable() throws Exception {
        String jdbcUrl = mysql.getJdbcUrl();
        try (Connection conn = DriverManager.getConnection(jdbcUrl, "user", "password");
             Statement stmt = conn.createStatement()) {
            stmt.execute("CREATE TABLE users (id INT, name VARCHAR(255))");
            assertTrue(true); // 表创建成功
        }
    }
}

在这个例子中,MySQLContainer 启动了一个 MySQL 8.0 实例,我们通过 JDBC 创建了一个简单的表。


容器配置

Testcontainers 允许开发者灵活配置容器,以满足特定需求。以下是几个常用配置选项:

端口映射

默认情况下,容器会随机映射端口到主机。你可以通过 withExposedPorts 指定端口:

1
2
3
@Container
public GenericContainer<?> redis = new GenericContainer<>("redis:6.2")
        .withExposedPorts(6379); // 映射 Redis 的 6379 端口

访问主机端口:redis.getMappedPort(6379)

环境变量

通过 withEnv 设置环境变量:

1
2
3
4
@Container
public MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
        .withEnv("MYSQL_ROOT_PASSWORD", "rootpass")
        .withEnv("MYSQL_DATABASE", "testdb");

启动命令

使用 withCommand 覆盖默认启动命令:

1
2
3
@Container
public GenericContainer<?> customContainer = new GenericContainer<>("nginx:latest")
        .withCommand("nginx -g 'daemon off;'");

生命周期管理

Testcontainers 自动管理容器的生命周期,但你也可以手动控制:

  • 启动和停止@Container 注解的容器会在测试类开始时启动,结束后停止。
  • 手动控制
    1
    2
    3
    4
    
    GenericContainer<?> container = new GenericContainer<>("redis:6.2");
    container.start();
    // 测试逻辑
    container.stop();
    
  • 容器重用:在调试时,可以启用重用以加快测试速度:
    1
    
    postgres.withReuse(true); // 需要配置 testcontainers.reuse.enable=true
    

注意:重用功能需谨慎使用,仅推荐用于本地开发。


与测试框架集成

JUnit 5

Testcontainers 与 JUnit 5 集成非常自然,通过 @Testcontainers@Container 注解即可使用。以下是完整示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import static org.junit.jupiter.api.Assertions.assertEquals;

@Testcontainers
public class PostgresJUnit5Test {

    @Container
    public PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("user")
            .withPassword("password");

    @Test
    public void testWithJUnit5() throws Exception {
        try (var conn = DriverManager.getConnection(postgres.getJdbcUrl(), "user", "password");
             var stmt = conn.createStatement()) {
            var rs = stmt.executeQuery("SELECT 2 + 2");
            rs.next();
            assertEquals(4, rs.getInt(1));
        }
    }
}

Spring Boot 测试

对于 Spring Boot 项目,Testcontainers 可以与 @SpringBootTest 结合使用。以下是一个测试 Spring Data JPA 的示例:

  1. 添加依赖:

    1
    2
    3
    4
    5
    6
    
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>1.19.7</version>
        <scope>test</scope>
    </dependency>
    
  2. 测试代码:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.testcontainers.containers.MySQLContainer;
    import org.testcontainers.junit.jupiter.Container;
    import org.testcontainers.junit.jupiter.Testcontainers;
    
    import javax.sql.DataSource;
    
    @Testcontainers
    @SpringBootTest
    public class SpringBootMySQLTest {
    
        @Container
        public static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0")
                .withDatabaseName("testdb");
    
        @Configuration
        static class TestConfig {
            @Bean
            public DataSource dataSource() {
                DriverManagerDataSource dataSource = new DriverManagerDataSource();
                dataSource.setUrl(mysql.getJdbcUrl());
                dataSource.setUsername(mysql.getUsername());
                dataSource.setPassword(mysql.getPassword());
                return dataSource;
            }
        }
    
        @Autowired
        private DataSource dataSource;
    
        @Test
        public void testSpringDataSource() throws Exception {
            try (var conn = dataSource.getConnection();
                 var stmt = conn.createStatement()) {
                stmt.execute("CREATE TABLE test (id INT)");
            }
        }
    }
    

在这个例子中,Spring Boot 使用 Testcontainers 提供的 MySQL 容器作为数据源。


调试技巧:日志输出

如果测试失败,可以通过容器日志定位问题:

1
mysql.withLogConsumer(output -> System.out.print(output.getUtf8String()));

运行测试时,MySQL 的日志会输出到控制台,帮助你排查问题。


总结

本篇介绍了 Testcontainers 的核心功能,包括常用容器类、配置选项、生命周期管理以及与 JUnit 和 Spring Boot 的集成。通过这些工具,你可以轻松应对大多数测试场景。下一篇文章将探讨进阶用法,如自定义容器和多容器网络。

updatedupdated2025-03-312025-03-31