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

QuarkusIntegrationTest运行时改属性:模拟外部端点超时测试

解决方案

核心问题分析

你遇到的两个核心问题本质是:

  1. @QuarkusIntegrationTest 启动独立Quarkus应用的时机早于测试方法执行,测试方法内设置System.setProperty完全无法影响已启动的应用配置;
  2. 多个@QuarkusIntegrationTest类运行冲突,通常是端口复用或应用实例复用导致的。

下面给出可落地的实现方案:


方案一:拆分测试类,用独立@TestProfile隔离场景

将正常测试和超时测试拆分为两个独立的集成测试类,分别绑定专属的测试Profile,从根源避免配置冲突。

1. 正常测试场景(测试1/2/3)

步骤1:创建WireMock专属测试Profile

public class WireMockNormalProfile implements TestProfile {
    @Override
    public Map<String, String> getConfigOverrides() {
        // 配置外部服务URL指向WireMock实例
        return Map.of(
            "external.service.url", "http://localhost:" + WireMockServer.DEFAULT_PORT + "/normal-api",
            "external.service.connect-timeout", "5000",
            "external.service.read-timeout", "5000"
        );
    }

    @Override
    public List<Class<? extends TestResource>> testResources() {
        // 绑定WireMock启动资源,自动启动/停止WireMock服务
        return List.of(WireMockNormalResource.class);
    }
}

步骤2:实现WireMock资源类(返回200响应)

public class WireMockNormalResource implements QuarkusTestResourceLifecycleManager {
    private WireMockServer wireMockServer;

    @Override
    public Map<String, String> start() {
        wireMockServer = new WireMockServer(WireMockServer.DEFAULT_PORT);
        wireMockServer.start();
        // 配置返回200的Stub
        wireMockServer.stubFor(get(urlEqualTo("/normal-api"))
                .willReturn(aResponse().withStatus(200).withBody("success")));
        return Map.of("wiremock.port", String.valueOf(wireMockServer.port()));
    }

    @Override
    public void stop() {
        if (wireMockServer != null) wireMockServer.stop();
    }
}

步骤3:编写正常测试类

@QuarkusIntegrationTest
@TestProfile(WireMockNormalProfile.class)
public class ExternalServiceNormalIT {

    @Inject
    ExternalServiceClient externalClient;

    @Test
    void test1() {
        String result = externalClient.callService();
        assertEquals("success", result);
    }

    @Test
    void test2() {
        // 测试逻辑
    }

    @Test
    void test3() {
        // 测试逻辑
    }
}

2. 超时测试场景(测试4)

步骤1:创建超时专属测试Profile

public class WireMockTimeoutProfile implements TestProfile {
    @Override
    public Map<String, String> getConfigOverrides() {
        return Map.of(
            "quarkus.http.port", "8082", // 指定独立端口,避免和正常测试冲突
            "external.service.url", "http://localhost:" + WireMockServer.DEFAULT_PORT + "/timeout-api",
            "external.service.connect-timeout", "1000", // 客户端超时设为1秒
            "external.service.read-timeout", "1000"
        );
    }

    @Override
    public List<Class<? extends TestResource>> testResources() {
        return List.of(WireMockTimeoutResource.class);
    }
}

步骤2:实现WireMock超时资源类

public class WireMockTimeoutResource implements QuarkusTestResourceLifecycleManager {
    private WireMockServer wireMockServer;

    @Override
    public Map<String, String> start() {
        wireMockServer = new WireMockServer(WireMockServer.DEFAULT_PORT);
        wireMockServer.start();
        // 配置延迟2秒的响应,超过客户端1秒超时阈值,触发超时异常
        wireMockServer.stubFor(get(urlEqualTo("/timeout-api"))
                .willReturn(aResponse().withFixedDelay(2000)));
        return Map.of("wiremock.port", String.valueOf(wireMockServer.port()));
    }

    @Override
    public void stop() {
        if (wireMockServer != null) wireMockServer.stop();
    }
}

步骤3:编写超时测试类

@QuarkusIntegrationTest
@TestProfile(WireMockTimeoutProfile.class)
public class ExternalServiceTimeoutIT {

    @Inject
    ExternalServiceClient externalClient;

    @Test
    void test4() {
        // 验证抛出预期的超时异常链
        ProcessingException exception = assertThrows(ProcessingException.class, 
            () -> externalClient.callService());
        
        assertTrue(exception.getCause() instanceof ConnectTimeoutException);
        assertTrue(exception.getCause().getMessage().contains("Read timed out"));
    }
}

关键注意事项

  1. 为什么不能在测试方法内切换Profile?
    @QuarkusIntegrationTest 会在测试类初始化前启动独立的Quarkus应用进程,此时Profile已经确定,测试方法内的System.setProperty无法影响已启动的应用。

  2. 多个集成测试类运行冲突解决
    通过在@TestProfile的getConfigOverrides()中设置不同的quarkus.http.port,让每个测试类启动独立端口的Quarkus应用,彻底避免端口冲突。

  3. 超时场景模拟的可靠性
    用WireMock设置延迟比使用真实无效地址更可控,不会受网络环境影响,确保测试稳定性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 03:02:14