From 19f8cdbde221b23d62ce819386f4071a8fa410d3 Mon Sep 17 00:00:00 2001 From: Daniel Garnier-Moiroux Date: Wed, 3 Sep 2025 10:53:21 +0200 Subject: [PATCH] Use static container in all client and transport tests - This allows reusing the containerized MCP server for all tests in a single class, significantly speeding up the tests, roughly 10x. Signed-off-by: Daniel Garnier-Moiroux --- ...ebClientStreamableHttpAsyncClientTests.java | 12 +++++++----- ...WebClientStreamableHttpSyncClientTests.java | 12 +++++++----- .../client/WebFluxSseMcpAsyncClientTests.java | 12 +++++++----- .../client/WebFluxSseMcpSyncClientTests.java | 12 +++++++----- .../WebFluxSseClientTransportTests.java | 18 ++++++++++-------- .../client/AbstractMcpAsyncClientTests.java | 16 ---------------- .../client/AbstractMcpSyncClientTests.java | 17 ----------------- .../client/AbstractMcpAsyncClientTests.java | 16 ---------------- .../client/AbstractMcpSyncClientTests.java | 17 ----------------- ...tpClientStreamableHttpAsyncClientTests.java | 14 ++++++++------ ...ttpClientStreamableHttpSyncClientTests.java | 13 +++++++------ .../client/HttpSseMcpAsyncClientTests.java | 14 ++++++++------ .../client/HttpSseMcpSyncClientTests.java | 15 ++++++++------- 13 files changed, 69 insertions(+), 119 deletions(-) diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpAsyncClientTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpAsyncClientTests.java index f8a16c153..8ef8177eb 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpAsyncClientTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpAsyncClientTests.java @@ -4,6 +4,8 @@ package io.modelcontextprotocol.client; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; import org.springframework.web.reactive.function.client.WebClient; import org.testcontainers.containers.GenericContainer; @@ -19,7 +21,7 @@ public class WebClientStreamableHttpAsyncClientTests extends AbstractMcpAsyncCli // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js streamableHttp") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -30,15 +32,15 @@ protected McpClientTransport createMcpTransport() { return WebClientStreamableHttpTransport.builder(WebClient.builder().baseUrl(host)).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - public void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpSyncClientTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpSyncClientTests.java index 5e9960d0e..1b252e27b 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpSyncClientTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebClientStreamableHttpSyncClientTests.java @@ -4,6 +4,8 @@ package io.modelcontextprotocol.client; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; import org.springframework.web.reactive.function.client.WebClient; import org.testcontainers.containers.GenericContainer; @@ -19,7 +21,7 @@ public class WebClientStreamableHttpSyncClientTests extends AbstractMcpSyncClien // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js streamableHttp") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -30,15 +32,15 @@ protected McpClientTransport createMcpTransport() { return WebClientStreamableHttpTransport.builder(WebClient.builder().baseUrl(host)).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - public void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpAsyncClientTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpAsyncClientTests.java index 0edf4cd54..e32222357 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpAsyncClientTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpAsyncClientTests.java @@ -6,6 +6,8 @@ import java.time.Duration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; import org.springframework.web.reactive.function.client.WebClient; import org.testcontainers.containers.GenericContainer; @@ -26,7 +28,7 @@ class WebFluxSseMcpAsyncClientTests extends AbstractMcpAsyncClientTests { // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js sse") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -37,15 +39,15 @@ protected McpClientTransport createMcpTransport() { return WebFluxSseClientTransport.builder(WebClient.builder().baseUrl(host)).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - public void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpSyncClientTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpSyncClientTests.java index 9b0959a35..a62dea267 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpSyncClientTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/WebFluxSseMcpSyncClientTests.java @@ -8,6 +8,8 @@ import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport; import io.modelcontextprotocol.spec.McpClientTransport; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -26,7 +28,7 @@ class WebFluxSseMcpSyncClientTests extends AbstractMcpSyncClientTests { // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js sse") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -37,15 +39,15 @@ protected McpClientTransport createMcpTransport() { return WebFluxSseClientTransport.builder(WebClient.builder().baseUrl(host)).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - protected void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransportTests.java b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransportTests.java index 1cf5dffe2..53f54d121 100644 --- a/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransportTests.java +++ b/mcp-spring/mcp-spring-webflux/src/test/java/io/modelcontextprotocol/client/transport/WebFluxSseClientTransportTests.java @@ -13,7 +13,9 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.spec.McpSchema; import io.modelcontextprotocol.spec.McpSchema.JSONRPCRequest; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; @@ -42,7 +44,7 @@ class WebFluxSseClientTransportTests { static String host = "http://localhost:3001"; @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js sse") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -95,15 +97,20 @@ public void simulateMessageEvent(String jsonMessage) { } - void startContainer() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } + @AfterAll + static void cleanup() { + container.stop(); + } + @BeforeEach void setUp() { - startContainer(); webClientBuilder = WebClient.builder().baseUrl(host); objectMapper = new ObjectMapper(); transport = new TestSseClientTransport(webClientBuilder, objectMapper); @@ -115,11 +122,6 @@ void afterEach() { if (transport != null) { assertThatCode(() -> transport.closeGracefully().block(Duration.ofSeconds(10))).doesNotThrowAnyException(); } - cleanup(); - } - - void cleanup() { - container.stop(); } @Test diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java index ea3739da5..8902a53b3 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java @@ -67,12 +67,6 @@ public abstract class AbstractMcpAsyncClientTests { abstract protected McpClientTransport createMcpTransport(); - protected void onStart() { - } - - protected void onClose() { - } - protected Duration getRequestTimeout() { return Duration.ofSeconds(14); } @@ -117,16 +111,6 @@ void withClient(McpClientTransport transport, Function void verifyNotificationSucceedsWithImplicitInitialization(Function> operation, String action) { withClient(createMcpTransport(), mcpAsyncClient -> { diff --git a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java index 175a0107c..8eb6ec248 100644 --- a/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java +++ b/mcp-test/src/main/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java @@ -66,12 +66,6 @@ public abstract class AbstractMcpSyncClientTests { abstract protected McpClientTransport createMcpTransport(); - protected void onStart() { - } - - protected void onClose() { - } - protected Duration getRequestTimeout() { return Duration.ofSeconds(14); } @@ -114,17 +108,6 @@ void withClient(McpClientTransport transport, Function void verifyNotificationSucceedsWithImplicitInitialization(Consumer operation, String action) { diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java index 3626d8ca0..af802df48 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpAsyncClientTests.java @@ -68,12 +68,6 @@ public abstract class AbstractMcpAsyncClientTests { abstract protected McpClientTransport createMcpTransport(); - protected void onStart() { - } - - protected void onClose() { - } - protected Duration getRequestTimeout() { return Duration.ofSeconds(14); } @@ -118,16 +112,6 @@ void withClient(McpClientTransport transport, Function void verifyNotificationSucceedsWithImplicitInitialization(Function> operation, String action) { withClient(createMcpTransport(), mcpAsyncClient -> { diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java index c74255060..4f6551199 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/AbstractMcpSyncClientTests.java @@ -67,12 +67,6 @@ public abstract class AbstractMcpSyncClientTests { abstract protected McpClientTransport createMcpTransport(); - protected void onStart() { - } - - protected void onClose() { - } - protected Duration getRequestTimeout() { return Duration.ofSeconds(14); } @@ -115,17 +109,6 @@ void withClient(McpClientTransport transport, Function void verifyNotificationSucceedsWithImplicitInitialization(Consumer operation, String action) { diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java index aef2ab8dd..647e27d30 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpAsyncClientTests.java @@ -4,6 +4,8 @@ package io.modelcontextprotocol.client; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -14,11 +16,11 @@ @Timeout(15) public class HttpClientStreamableHttpAsyncClientTests extends AbstractMcpAsyncClientTests { - private String host = "http://localhost:3001"; + private static String host = "http://localhost:3001"; // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js streamableHttp") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -30,15 +32,15 @@ protected McpClientTransport createMcpTransport() { return HttpClientStreamableHttpTransport.builder(host).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - public void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java index 6f3b58e3d..e798db82e 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/HttpClientStreamableHttpSyncClientTests.java @@ -7,6 +7,8 @@ import java.net.URI; import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.testcontainers.containers.GenericContainer; @@ -17,7 +19,6 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpClientTransport; -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; @@ -31,7 +32,7 @@ public class HttpClientStreamableHttpSyncClientTests extends AbstractMcpSyncClie // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js streamableHttp") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -44,15 +45,15 @@ protected McpClientTransport createMcpTransport() { return HttpClientStreamableHttpTransport.builder(host).httpRequestCustomizer(requestCustomizer).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - public void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java index 6cb3f7b65..8827f7ec7 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpAsyncClientTests.java @@ -4,6 +4,8 @@ package io.modelcontextprotocol.client; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Timeout; import org.testcontainers.containers.GenericContainer; import org.testcontainers.containers.wait.strategy.Wait; @@ -19,11 +21,11 @@ @Timeout(15) class HttpSseMcpAsyncClientTests extends AbstractMcpAsyncClientTests { - String host = "http://localhost:3004"; + private static String host = "http://localhost:3004"; // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js sse") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -34,15 +36,15 @@ protected McpClientTransport createMcpTransport() { return HttpClientSseClientTransport.builder(host).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - protected void onClose() { + @AfterAll + static void stopContainer() { container.stop(); } diff --git a/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java b/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java index 4091d7a5e..38c6fdc4e 100644 --- a/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java +++ b/mcp/src/test/java/io/modelcontextprotocol/client/HttpSseMcpSyncClientTests.java @@ -7,6 +7,8 @@ import java.net.URI; import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; import org.testcontainers.containers.GenericContainer; @@ -17,7 +19,6 @@ import io.modelcontextprotocol.common.McpTransportContext; import io.modelcontextprotocol.spec.McpClientTransport; -import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -33,11 +34,11 @@ @Timeout(15) // Giving extra time beyond the client timeout class HttpSseMcpSyncClientTests extends AbstractMcpSyncClientTests { - String host = "http://localhost:3003"; + static String host = "http://localhost:3003"; // Uses the https://github.com/tzolov/mcp-everything-server-docker-image @SuppressWarnings("resource") - GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") + static GenericContainer container = new GenericContainer<>("docker.io/tzolov/mcp-everything-server:v2") .withCommand("node dist/index.js sse") .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) .withExposedPorts(3001) @@ -50,15 +51,15 @@ protected McpClientTransport createMcpTransport() { return HttpClientSseClientTransport.builder(host).httpRequestCustomizer(requestCustomizer).build(); } - @Override - protected void onStart() { + @BeforeAll + static void startContainer() { container.start(); int port = container.getMappedPort(3001); host = "http://" + container.getHost() + ":" + port; } - @Override - protected void onClose() { + @AfterAll + static void stopContainer() { container.stop(); }