From 5d460bc602536934bc712c8343ef895bdfb6ae3a Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 17 Oct 2025 19:12:59 +0100 Subject: [PATCH] Use RestTestClient rather than REST Assured Closes gh-47686 --- .../build.gradle | 3 +- ...nectionDetailsFactoryIntegrationTests.java | 70 +++++++------------ ...nectionDetailsFactoryIntegrationTests.java | 52 ++++++++------ 3 files changed, 61 insertions(+), 64 deletions(-) diff --git a/module/spring-boot-micrometer-metrics/build.gradle b/module/spring-boot-micrometer-metrics/build.gradle index ef685d15bf2..cfaa0830b33 100644 --- a/module/spring-boot-micrometer-metrics/build.gradle +++ b/module/spring-boot-micrometer-metrics/build.gradle @@ -66,7 +66,8 @@ dependencies { dockerTestImplementation(project(":test-support:spring-boot-docker-test-support")) dockerTestImplementation(testFixtures(project(":core:spring-boot-docker-compose"))) - dockerTestImplementation("io.rest-assured:rest-assured") + dockerTestImplementation("org.apache.httpcomponents.client5:httpclient5") + dockerTestImplementation("org.springframework:spring-web") dockerTestImplementation("org.testcontainers:testcontainers-junit-jupiter") testFixturesImplementation(project(":test-support:spring-boot-test-support")) diff --git a/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java b/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java index 7b39c2e9f2b..b60cf69ea4e 100644 --- a/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java +++ b/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.micrometer.metrics.testcontainers.otlp; +import java.net.URI; import java.time.Duration; import io.micrometer.core.instrument.Clock; @@ -24,8 +25,6 @@ import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; -import io.restassured.RestAssured; -import io.restassured.response.Response; import org.awaitility.Awaitility; import org.junit.jupiter.api.Test; import org.testcontainers.grafana.LgtmStackContainer; @@ -41,8 +40,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; - -import static org.assertj.core.api.Assertions.assertThat; +import org.springframework.test.web.servlet.client.RestTestClient; /** * Tests for {@link GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactory}. @@ -68,53 +66,39 @@ class GrafanaOpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTes Gauge.builder("test.gauge", () -> 12).register(this.meterRegistry); Timer.builder("test.timer").register(this.meterRegistry).record(Duration.ofMillis(123)); DistributionSummary.builder("test.distributionsummary").register(this.meterRegistry).record(24); - Awaitility.given() .pollInterval(Duration.ofSeconds(2)) .atMost(Duration.ofSeconds(10)) .ignoreExceptions() .untilAsserted(() -> { - Response response = RestAssured.given() - .queryParam("query", "{job=\"test\"}") - .get("%s/api/v1/query".formatted(container.getPrometheusHttpUrl())) - .prettyPeek() - .thenReturn(); - assertThat(response.getStatusCode()).isEqualTo(200); - assertThat(response.body() - .jsonPath() - .getList("data.result.find { it.metric.__name__ == 'test_counter_total' }.value")).contains("42"); - assertThat(response.body() - .jsonPath() - .getList("data.result.find { it.metric.__name__ == 'test_gauge' }.value")).contains("12"); - assertThat(response.body() - .jsonPath() - .getList("data.result.find { it.metric.__name__ == 'test_timer_milliseconds_count' }.value")) - .contains("1"); - assertThat(response.body() - .jsonPath() - .getList("data.result.find { it.metric.__name__ == 'test_timer_milliseconds_sum' }.value")) - .contains("123"); - assertThat(response.body() - .jsonPath() - .getList( - "data.result.find { it.metric.__name__ == 'test_timer_milliseconds_bucket' & it.metric.le == '+Inf' }.value")) - .contains("1"); - assertThat(response.body() - .jsonPath() - .getList("data.result.find { it.metric.__name__ == 'test_distributionsummary_count' }.value")) - .contains("1"); - assertThat(response.body() - .jsonPath() - .getList("data.result.find { it.metric.__name__ == 'test_distributionsummary_sum' }.value")) - .contains("24"); - assertThat(response.body() - .jsonPath() - .getList( - "data.result.find { it.metric.__name__ == 'test_distributionsummary_bucket' & it.metric.le == '+Inf' }.value")) - .contains("1"); + RestTestClient restClient = RestTestClient.bindToServer().build(); + restClient.get() + .uri(URI.create(container.getPrometheusHttpUrl() + "/api/v1/query?query=%7Bjob=%22test%22%7D")) + .exchange() + .expectStatus() + .isOk() + .expectBody() + .jsonPath(metricWithValue("test_counter_total", "42")) + .exists() + .jsonPath(metricWithValue("test_timer_milliseconds_count", "1")) + .exists() + .jsonPath(metricWithValue("test_timer_milliseconds_sum", "123")) + .exists() + .jsonPath(metricWithValue("test_timer_milliseconds_bucket", "1")) + .exists() + .jsonPath(metricWithValue("test_distributionsummary_count", "1")) + .exists() + .jsonPath(metricWithValue("test_distributionsummary_sum", "24")) + .exists() + .jsonPath(metricWithValue("test_distributionsummary_bucket", "1")) + .exists(); }); } + private String metricWithValue(String metric, String value) { + return "$.data.result[?(@.metric.__name__==\"%s\" && \"%s\" in @.value)]".formatted(metric, value); + } + @Configuration(proxyBeanMethods = false) @ImportAutoConfiguration(OtlpMetricsExportAutoConfiguration.class) static class TestConfiguration { diff --git a/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java b/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java index 1fb6a5bfb4b..0ad4878940b 100644 --- a/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java +++ b/module/spring-boot-micrometer-metrics/src/dockerTest/java/org/springframework/boot/micrometer/metrics/testcontainers/otlp/OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.micrometer.metrics.testcontainers.otlp; import java.time.Duration; +import java.util.function.Consumer; import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Counter; @@ -24,9 +25,8 @@ import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; -import io.restassured.RestAssured; -import io.restassured.response.Response; import org.awaitility.Awaitility; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; @@ -40,12 +40,13 @@ import org.springframework.boot.testcontainers.service.connection.ServiceConnect import org.springframework.boot.testsupport.container.TestImage; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; +import org.springframework.test.web.servlet.client.RestTestClient; +import org.springframework.test.web.servlet.client.RestTestClient.ResponseSpec; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.matchesPattern; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link OpenTelemetryMetricsContainerConnectionDetailsFactory}. @@ -59,7 +60,8 @@ import static org.hamcrest.Matchers.matchesPattern; @Testcontainers(disabledWithoutDocker = true) class OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests { - private static final String OPENMETRICS_001 = "application/openmetrics-text; version=0.0.1; charset=utf-8"; + private static final MediaType OPENMETRICS_001 = MediaType + .parseMediaType("application/openmetrics-text; version=0.0.1; charset=utf-8"); private static final String CONFIG_FILE_NAME = "collector-config.yml"; @@ -81,23 +83,33 @@ class OpenTelemetryMetricsContainerConnectionDetailsFactoryIntegrationTests { DistributionSummary.builder("test.distributionsummary").register(this.meterRegistry).record(24); Awaitility.await() .atMost(Duration.ofSeconds(30)) - .untilAsserted(() -> whenPrometheusScraped().then() - .statusCode(200) + .untilAsserted(() -> whenPrometheusScraped().expectStatus() + .isOk() + .expectHeader() .contentType(OPENMETRICS_001) - .body(endsWith("# EOF\n"), containsString( - "{job=\"test\",service_name=\"test\",telemetry_sdk_language=\"java\",telemetry_sdk_name=\"io.micrometer\""), - matchesPattern("(?s)^.*test_counter\\{.+} 42\\.0\\n.*$"), - matchesPattern("(?s)^.*test_gauge\\{.+} 12\\.0\\n.*$"), - matchesPattern("(?s)^.*test_timer_count\\{.+} 1\\n.*$"), - matchesPattern("(?s)^.*test_timer_sum\\{.+} 123\\.0\\n.*$"), - matchesPattern("(?s)^.*test_timer_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$"), - matchesPattern("(?s)^.*test_distributionsummary_count\\{.+} 1\\n.*$"), - matchesPattern("(?s)^.*test_distributionsummary_sum\\{.+} 24\\.0\\n.*$"), - matchesPattern("(?s)^.*test_distributionsummary_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$"))); + .expectBody(String.class) + .value((Consumer<@Nullable String>) (body) -> assertThat(body).endsWith("# EOF\n") + .contains( + "{job=\"test\",service_name=\"test\",telemetry_sdk_language=\"java\",telemetry_sdk_name=\"io.micrometer\"") + .matches("(?s)^.*test_counter\\{.+} 42\\.0\\n.*$") + .matches("(?s)^.*test_gauge\\{.+} 12\\.0\\n.*$") + .matches("(?s)^.*test_timer_count\\{.+} 1\\n.*$") + .matches("(?s)^.*test_timer_sum\\{.+} 123\\.0\\n.*$") + .matches("(?s)^.*test_timer_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$") + .matches("(?s)^.*test_distributionsummary_count\\{.+} 1\\n.*$") + .matches("(?s)^.*test_distributionsummary_sum\\{.+} 24\\.0\\n.*$") + .matches("(?s)^.*test_distributionsummary_bucket\\{.+,le=\"\\+Inf\"} 1\\n.*$"))); + } - private Response whenPrometheusScraped() { - return RestAssured.given().port(container.getMappedPort(9090)).accept(OPENMETRICS_001).when().get("/metrics"); + private ResponseSpec whenPrometheusScraped() { + return RestTestClient.bindToServer() + .baseUrl("http://" + container.getHost() + ":" + container.getMappedPort(9090)) + .build() + .get() + .uri("/metrics") + .accept(OPENMETRICS_001) + .exchange(); } @Configuration(proxyBeanMethods = false)