如何创建Quarkus自身应用的测试资源以模拟多节点通信?
在Quarkus中测试多节点HTTP通信的解决方案
核心思路
Quarkus的QuarkusTestResourceLifecycleManager确实可用于管理测试所需的外部资源,包括启动额外的应用实例。但直接调用Quarkus.run()会与测试本身的Quarkus应用实例冲突,正确做法是使用Quarkus的Launcher API启动独立的、指定端口的应用实例。
实现步骤
1. 编写自定义TestResource管理多实例
创建MultiNodeTestResource类,通过Launcher启动指定端口的应用实例,并记录实例的端口与地址信息:
import io.quarkus.bootstrap.app.QuarkusBootstrap; import io.quarkus.bootstrap.app.QuarkusLauncher; import io.quarkus.test.common.QuarkusTestResourceLifecycleManager; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; public class MultiNodeTestResource implements QuarkusTestResourceLifecycleManager { private final CopyOnWriteArrayList<QuarkusLauncher> launchers = new CopyOnWriteArrayList<>(); private final Map<String, String> config = new HashMap<>(); @Override public Map<String, String> start() { // 启动第一个实例,端口8082 launchInstance(8082, "node1"); // 启动第二个实例,端口8083 launchInstance(8083, "node2"); return config; } private void launchInstance(int port, String nodeName) { try { QuarkusBootstrap bootstrap = QuarkusBootstrap.builder() .setApplicationRoot(QuarkusBootstrap.DefaultApplicationRoot.builder() .addModuleClassPathElement(Thread.currentThread().getContextClassLoader()) .build()) .setArguments("-Dquarkus.http.port=" + port) .build(); QuarkusLauncher launcher = bootstrap.createLauncher(); launcher.start(); launchers.add(launcher); config.put(nodeName + ".port", String.valueOf(port)); config.put(nodeName + ".url", "http://localhost:" + port); } catch (Exception e) { throw new RuntimeException("Failed to launch Quarkus instance", e); } } @Override public void stop() { // 停止所有启动的实例 for (QuarkusLauncher launcher : launchers) { try { launcher.stop(); } catch (Exception e) { // 忽略停止时的异常,避免影响测试结束 } } } }
2. 在测试类中使用该TestResource
通过@TestResource注解引入自定义资源,再通过@ConfigProperty获取各个节点的地址,编写节点间通信测试:
import io.quarkus.test.common.QuarkusTestResource; import io.quarkus.test.junit.QuarkusTest; import jakarta.inject.Inject; import org.eclipse.microprofile.config.inject.ConfigProperty; import org.junit.jupiter.api.Test; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @QuarkusTest @TestResource(MultiNodeTestResource.class) public class MultiNodeCommunicationTest { @Inject @ConfigProperty(name = "node1.url") String node1Url; @Inject @ConfigProperty(name = "node2.url") String node2Url; private final HttpClient httpClient = HttpClient.newHttpClient(); @Test void testNodeToNodeCommunication() throws Exception { // 测试node1向node2发送请求 HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(node2Url + "/some-endpoint")) .GET() .build(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); // 断言响应符合预期 assert response.statusCode() == 200; } }
关键注意事项
- 端口冲突:若需动态避免端口冲突,可使用
SocketUtils.findAvailableTcpPort()获取可用端口后传递给实例。 - 资源清理:必须在
stop()方法中停止所有启动的实例,避免测试后残留进程。 - 类路径配置:确保
QuarkusBootstrap正确设置应用类路径,否则启动的实例可能无法找到应用代码。
内容的提问来源于stack exchange,提问作者user23492895
相关产品推荐
相关产品推荐

