You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用TestContainers识别本地Docker容器并实现JUnit集成测试

用TestContainers实现JUnit集成测试的两种场景方案

一、为什么你的代码没连接到本地容器

你之前创建GenericContainer实例的方式,默认是拉取镜像并启动新容器,而不是关联本地已运行的容器。TestContainers的核心设计是管理临时测试容器,不会自动识别并连接到外部已启动的实例。

二、判断本地Docker容器是否运行

可以通过TestContainers封装的DockerClient直接调用Docker API来检查容器状态:

import org.testcontainers.DockerClientFactory;
import com.github.dockerjava.api.model.Container;

public boolean isContainerRunning(String containerNameOrId) {
    var dockerClient = DockerClientFactory.instance().client();
    try {
        // 遍历所有运行中的容器,匹配名称或ID
        for (Container container : dockerClient.listContainersCmd().withStatusFilter("running").exec()) {
            boolean matchesName = container.getNames()[0].contains(containerNameOrId);
            boolean matchesId = container.getId().startsWith(containerNameOrId);
            if (matchesName || matchesId) {
                return true;
            }
        }
        return false;
    } catch (Exception e) {
        // 处理Docker连接异常,默认返回未运行
        return false;
    }
}

在测试初始化阶段调用这个方法,就能确认目标容器的运行状态。

三、场景1:连接本地已运行的Docker容器

TestContainers没有直接“附着”到外部容器的API,但可以通过以下方式实现连接:

方式1:直接使用端口映射访问

如果本地容器已经做了端口映射到主机,直接用主机端口连接即可:

// 假设myContainer将内部5432端口映射到主机的5432端口
String jdbcUrl = "jdbc:postgresql://localhost:5432/mydb";

方式2:通过Docker网络别名访问

如果容器在自定义网络(比如你指定的myNetwork)中,可以利用容器的网络别名直接访问(无需端口映射):

// 假设容器在myNetwork中的别名为my-container
String jdbcUrl = "jdbc:postgresql://my-container:5432/mydb";

// 测试类需配置使用同一网络,确保测试代码能接入该网络
private static final Network MY_NETWORK = Network.builder()
        .createNetworkParamsModifier(params -> params.withName("myNetwork"))
        .build();

方式3:用ExternalResource封装连接逻辑

要贴合JUnit的测试生命周期,可以自定义ExternalResource来管理外部容器的可用性:

import org.junit.rules.ExternalResource;

public class ExistingContainerResource extends ExternalResource {
    private final String containerName;
    private boolean containerAvailable;

    public ExistingContainerResource(String containerName) {
        this.containerName = containerName;
    }

    @Override
    protected void before() throws Throwable {
        containerAvailable = isContainerRunning(containerName);
        if (!containerAvailable) {
            throw new IllegalStateException(String.format("容器 %s 未处于运行状态", containerName));
        }
    }

    // 对外提供容器的连接信息
    public String getDatabaseUrl() {
        return "jdbc:postgresql://my-container:5432/mydb";
    }
}

// 测试中使用该Rule
@Rule
public ExistingContainerResource existingContainer = new ExistingContainerResource("myContainer");

@Test
public void testWithExistingContainer() {
    String dbUrl = existingContainer.getDatabaseUrl();
    // 执行测试逻辑
}

四、场景2:由TestContainers启动容器

这是TestContainers的常规用法,调整你的初始代码即可:

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.junit.jupiter.api.Test;

@Testcontainers
public class ManagedContainerTest {

    @Container
    private static final GenericContainer<?> MY_CONTAINER = new GenericContainer<>(DockerImageName.parse("myContainer"))
            .withNetworkMode("myNetwork")
            .withExposedPorts(5432); // 暴露容器内部端口

    @Test
    public void testWithManagedContainer() {
        // 获取容器映射到主机的随机端口
        Integer mappedPort = MY_CONTAINER.getMappedPort(5432);
        String jdbcUrl = String.format("jdbc:postgresql://localhost:%d/mydb", mappedPort);
        // 执行测试逻辑
    }
}

五、整合两种场景的测试逻辑

可以通过条件判断,自动选择使用本地已运行容器或TestContainers启动的容器:

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

public class HybridContainerTest {
    private static String jdbcUrl;

    @BeforeAll
    public static void setupContainer() {
        if (isContainerRunning("myContainer")) {
            // 使用本地已运行容器
            jdbcUrl = "jdbc:postgresql://my-container:5432/mydb";
        } else {
            // 启动TestContainers管理的容器
            GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("myContainer"))
                    .withNetworkMode("myNetwork")
                    .withExposedPorts(5432)
                    .start();
            jdbcUrl = String.format("jdbc:postgresql://localhost:%d/mydb", container.getMappedPort(5432));
        }
    }

    @Test
    public void testHybridScenario() {
        // 使用统一的jdbcUrl执行测试逻辑
    }
}

内容的提问来源于stack exchange,提问作者rupweb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 13:10:26