QuarkusIntegrationTest运行时改属性:模拟外部端点超时测试
解决方案
核心问题分析
你遇到的两个核心问题本质是:
@QuarkusIntegrationTest启动独立Quarkus应用的时机早于测试方法执行,测试方法内设置System.setProperty完全无法影响已启动的应用配置;- 多个
@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")); } }
关键注意事项
为什么不能在测试方法内切换Profile?
@QuarkusIntegrationTest会在测试类初始化前启动独立的Quarkus应用进程,此时Profile已经确定,测试方法内的System.setProperty无法影响已启动的应用。多个集成测试类运行冲突解决
通过在@TestProfile的getConfigOverrides()中设置不同的quarkus.http.port,让每个测试类启动独立端口的Quarkus应用,彻底避免端口冲突。超时场景模拟的可靠性
用WireMock设置延迟比使用真实无效地址更可控,不会受网络环境影响,确保测试稳定性。
内容的提问来源于stack exchange,提问作者WesternGun
相关产品推荐
相关产品推荐

