Get Spring Boot management port at runtime when management.port=0
Asked Answered
S

3

7

I'm looking for advice on how to get the port that was assigned to the embedded Tomcat that is serving the actuator endpoint when setting management.port property to 0 in integration tests.

I'm using Spring Boot 1.3.2 with the following application.yml configuration:

server.port: 8080
server.contextPath: /my-app-context-path

management.port: 8081
management.context-path: /manage

...

My integration tests are then annotated with @WebIntegrationTest, setting the ports shown above to 0:

@WebIntegrationTest({ "server.port=0", "management.port=0" })

And the following utility class should be used to get access to the application configuration when doing full integration tests:

@Component
@Profile("testing")
class TestserverInfo {
    
    @Value( '${server.contextPath:}' )
    private String contextPath;

    @Autowired
    private EmbeddedWebApplicationContext server;
    
    @Autowired
    private ManagementServerProperties managementServerProperties
    
    
    public String getBasePath() {
        final int serverPort = server.embeddedServletContainer.port
        
        return "http://localhost:${serverPort}${contextPath}"
    }
    
    public String getManagementPath() {
        // The following wont work here:
        // server.embeddedServletContainer.port -> regular server port
        // management.port -> is zero just as server.port as i want random ports

        final int managementPort = // how can i get this one ?
        final String managementPath = managementServerProperties.getContextPath()
        
        return "http://localhost:${managementPort}${managementPath}"
    }
}

I already know the standard port can be get by using the local.server.port and there seems to be some equivalent for the management endpoint named local.management.port. But that one seems to have a different meaning. The official documentation doesn't mention a way to do this.

Is there currently any undocumented way to get a hand on that management port?


Solution Edit

As I am using the Spock Framework and spock-spring module for testing my Spring Boot application, I have to initialize the application like this:

@ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyApplication.class)

Somehow spock-spring or the test-initialization seems to affect the evaluation of the @Value Annotation so that @Value("${local.management.port}") resulted in

java.lang.IllegalArgumentException: Could not resolve placeholder 'local.management.port' in string value "${local.management.port}"

With your solution I knew the property existed, so I simply use the Spring Environment directly to retrieve the property-value at test runtime:

@Autowired
ManagementServerProperties managementServerProperties

@Autowired
Environment environment

public String getManagementPath() {
    final int managementPort = environment.getProperty('local.management.port', Integer.class)
    final String managementPath = managementServerProperties.getContextPath()
    
    return "http://localhost:${managementPort}${managementPath}"
}
Secondhand answered 12/4, 2016 at 8:7 Comment(0)
L
6

This is how I've done it, copied straight from my test class (I use RestAssured for assertions):

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;

import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static com.jayway.restassured.RestAssured.get;
import static org.hamcrest.CoreMatchers.equalTo;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest(randomPort = true, value = {"management.port=0", "management.context-path=/admin"})
@DirtiesContext
public class ActuatorEndpointTest {

    @Value("${local.management.port}")
    private int localManagementPort;

    @Test
    public void actuatorHealthEndpointIsAvailable() throws Exception {

        String healthUrl = "http://localhost:" + localManagementPort + "/admin/health";
        get(healthUrl)
                .then()
                .assertThat().body("status", equalTo("UP"));
    }



}
Loggins answered 12/4, 2016 at 9:43 Comment(2)
Thank you @dave-bower, seeing your solution hinted me in the right direction.Secondhand
My problem was, that I am using the Spock-Framework and Spock-Spring, where this property does not resolve when using the @Value Annotation. Propably because I'm initializing my Tests using @ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyApplication.class). I will edit my question and add the solution for my Spock-Testing Environment there.Secondhand
T
13

Management port needs to be set to 0 via the @SpringBootTest's properties field and then to get the port in the tests use @LocalServerPort and/or @LocalManagementPort annotations.

Examples:

From Spring Boot 2.0.0:
properties = {"management.server.port=0"}

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = {
        "management.server.port=0" })
public class HealthCheckIT {

    @Autowired
    private WebTestClient webTestClient;

    @LocalManagementPort
    int managementPort;

    @Test
    public void testManagementPort() {
        webTestClient
                .get().uri("http://localhost:" + managementPort + "/actuator/health")
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk();
    }
}

For older versions: [1.4.0 - 1.5.x]:
properties = {"management.port=0"}

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
    "management.port=0", "management.context-path=/admin" })
public class SampleTest {

    @LocalServerPort
    int port;

    @LocalManagementPort
    int managementPort;
Tallyho answered 22/12, 2016 at 15:36 Comment(2)
this needs import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;Show
On Spring Boot 2.0.0 the property management.port was changed to management.server.portFew
L
6

This is how I've done it, copied straight from my test class (I use RestAssured for assertions):

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.WebIntegrationTest;

import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static com.jayway.restassured.RestAssured.get;
import static org.hamcrest.CoreMatchers.equalTo;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
@WebIntegrationTest(randomPort = true, value = {"management.port=0", "management.context-path=/admin"})
@DirtiesContext
public class ActuatorEndpointTest {

    @Value("${local.management.port}")
    private int localManagementPort;

    @Test
    public void actuatorHealthEndpointIsAvailable() throws Exception {

        String healthUrl = "http://localhost:" + localManagementPort + "/admin/health";
        get(healthUrl)
                .then()
                .assertThat().body("status", equalTo("UP"));
    }



}
Loggins answered 12/4, 2016 at 9:43 Comment(2)
Thank you @dave-bower, seeing your solution hinted me in the right direction.Secondhand
My problem was, that I am using the Spock-Framework and Spock-Spring, where this property does not resolve when using the @Value Annotation. Propably because I'm initializing my Tests using @ContextConfiguration(loader = SpringApplicationContextLoader.class, classes = MyApplication.class). I will edit my question and add the solution for my Spock-Testing Environment there.Secondhand
D
0

This is an update on @magiccrafter answer which requires the following to work properly

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    <scope>test</scope>
</dependency>

and it requires this annotation as well

@AutoConfigureWebTestClient

Dewdrop answered 8/8, 2021 at 12:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.