Skip to content

Commit d5a197f

Browse files
committed
Support random actuator port in SpringBootTest
This commit changes @SpringBootTest(randomPort = true) to generate a random port for the actuator endpoints if the management server runs on a different port from the main server. Closes gh-4424
1 parent 01e61d8 commit d5a197f

File tree

4 files changed

+234
-0
lines changed

4 files changed

+234
-0
lines changed

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6491,6 +6491,10 @@ test method by default. However, as using this arrangement with either `RANDOM_P
64916491
run in separate threads and, thus, in separate transactions. Any transaction initiated on
64926492
the server does not roll back in this case.
64936493

6494+
NOTE: `@SpringBootTest` with `webEnvironment = WebEnvironment.RANDOM_PORT` will also
6495+
start the management server on a separate random port if your application uses a different
6496+
port for the management server.
6497+
64946498

64956499

64966500
[[boot-features-testing-spring-boot-applications-detecting-web-app-type]]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.boot.test.web;
17+
18+
import java.util.Objects;
19+
20+
import org.springframework.boot.SpringApplication;
21+
import org.springframework.boot.env.EnvironmentPostProcessor;
22+
import org.springframework.core.env.ConfigurableEnvironment;
23+
import org.springframework.core.env.MapPropertySource;
24+
import org.springframework.test.context.support.TestPropertySourceUtils;
25+
26+
/**
27+
* {@link EnvironmentPostProcessor} implementation to start the management context on a
28+
* random port if the main server's port is 0 and the management context is expected on a
29+
* different port.
30+
*
31+
* @author Madhura Bhave
32+
* @since 2.1.0
33+
*/
34+
public class SpringBootTestRandomPortEnvironmentPostProcessor
35+
implements EnvironmentPostProcessor {
36+
37+
private static final String MANAGEMENT_PORT_PROPERTY = "management.server.port";
38+
39+
private static final String SERVER_PORT_PROPERTY = "server.port";
40+
41+
@Override
42+
public void postProcessEnvironment(ConfigurableEnvironment environment,
43+
SpringApplication application) {
44+
MapPropertySource source = (MapPropertySource) environment.getPropertySources()
45+
.get(TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
46+
if (isTestServerPortRandom(source)) {
47+
if (source.getProperty(MANAGEMENT_PORT_PROPERTY) == null) {
48+
String managementPort = getPort(environment, MANAGEMENT_PORT_PROPERTY,
49+
null);
50+
String serverPort = getPort(environment, SERVER_PORT_PROPERTY, "8080");
51+
if (managementPort != null && !managementPort.equals("-1")) {
52+
if (!managementPort.equals(serverPort)) {
53+
source.getSource().put(MANAGEMENT_PORT_PROPERTY, "0");
54+
}
55+
else {
56+
source.getSource().put(MANAGEMENT_PORT_PROPERTY, "");
57+
}
58+
}
59+
}
60+
}
61+
62+
}
63+
64+
private boolean isTestServerPortRandom(MapPropertySource source) {
65+
return (source != null && "0".equals(source.getProperty(SERVER_PORT_PROPERTY)));
66+
}
67+
68+
private String getPort(ConfigurableEnvironment environment, String property,
69+
String defaultValue) {
70+
return environment.getPropertySources().stream()
71+
.filter((source) -> !source.getName().equals(
72+
TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME))
73+
.map((source) -> (String) source.getProperty(property))
74+
.filter(Objects::nonNull).findFirst().orElse(defaultValue);
75+
}
76+
77+
}

spring-boot-project/spring-boot-test/src/main/resources/META-INF/spring.factories

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizer
1111
org.springframework.test.context.TestExecutionListener=\
1212
org.springframework.boot.test.mock.mockito.MockitoTestExecutionListener,\
1313
org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener
14+
15+
# Environment Post Processors
16+
org.springframework.boot.env.EnvironmentPostProcessor=\
17+
org.springframework.boot.test.web.SpringBootTestRandomPortEnvironmentPostProcessor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright 2012-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.boot.test.web;
17+
18+
import java.util.HashMap;
19+
import java.util.Map;
20+
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
24+
import org.springframework.core.env.MapPropertySource;
25+
import org.springframework.core.env.MutablePropertySources;
26+
import org.springframework.mock.env.MockEnvironment;
27+
import org.springframework.test.context.support.TestPropertySourceUtils;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
/**
32+
* Tests for {@link SpringBootTestRandomPortEnvironmentPostProcessor}.
33+
*
34+
* @author Madhura Bhave
35+
*/
36+
public class SpringBootTestRandomPortEnvironmentPostProcessorTests {
37+
38+
private SpringBootTestRandomPortEnvironmentPostProcessor postProcessor = new SpringBootTestRandomPortEnvironmentPostProcessor();
39+
40+
private MockEnvironment environment;
41+
42+
private MutablePropertySources propertySources;
43+
44+
@Before
45+
public void setup() {
46+
this.environment = new MockEnvironment();
47+
this.propertySources = this.environment.getPropertySources();
48+
}
49+
50+
@Test
51+
public void postProcessWhenServerAndManagementPortIsZeroInTestPropertySource() {
52+
addTestPropertySource("0", "0");
53+
this.environment.setProperty("management.server.port", "0");
54+
this.postProcessor.postProcessEnvironment(this.environment, null);
55+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
56+
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
57+
}
58+
59+
@Test
60+
public void postProcessWhenTestServerAndTestManagementPortAreNonZero() {
61+
addTestPropertySource("8080", "8081");
62+
this.environment.setProperty("server.port", "8080");
63+
this.environment.setProperty("management.server.port", "8081");
64+
this.postProcessor.postProcessEnvironment(this.environment, null);
65+
assertThat(this.environment.getProperty("server.port")).isEqualTo("8080");
66+
assertThat(this.environment.getProperty("management.server.port"))
67+
.isEqualTo("8081");
68+
}
69+
70+
@Test
71+
public void postProcessWhenTestServerPortIsZeroAndTestManagementPortIsNotNull() {
72+
addTestPropertySource("0", "8080");
73+
this.postProcessor.postProcessEnvironment(this.environment, null);
74+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
75+
assertThat(this.environment.getProperty("management.server.port"))
76+
.isEqualTo("8080");
77+
}
78+
79+
@Test
80+
public void postProcessWhenTestServerPortIsZeroAndManagementPortIsNull() {
81+
addTestPropertySource("0", null);
82+
this.postProcessor.postProcessEnvironment(this.environment, null);
83+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
84+
assertThat(this.environment.getProperty("management.server.port"))
85+
.isEqualTo(null);
86+
}
87+
88+
@Test
89+
public void postProcessWhenTestServerPortIsZeroAndManagementPortIsNotNullAndSameInProduction() {
90+
addTestPropertySource("0", null);
91+
Map<String, Object> other = new HashMap<>();
92+
other.put("server.port", "8081");
93+
other.put("management.server.port", "8081");
94+
MapPropertySource otherSource = new MapPropertySource("other", other);
95+
this.propertySources.addLast(otherSource);
96+
this.postProcessor.postProcessEnvironment(this.environment, null);
97+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
98+
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("");
99+
}
100+
101+
@Test
102+
public void postProcessWhenTestServerPortIsZeroAndManagementPortIsNotNullAndDefaultSameInProduction() {
103+
// mgmt port is 8080 which means its on the same port as main server since that is
104+
// null in app properties
105+
addTestPropertySource("0", null);
106+
Map<String, Object> other = new HashMap<>();
107+
other.put("management.server.port", "8080");
108+
MapPropertySource otherSource = new MapPropertySource("other", other);
109+
this.propertySources.addLast(otherSource);
110+
this.postProcessor.postProcessEnvironment(this.environment, null);
111+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
112+
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("");
113+
}
114+
115+
@Test
116+
public void postProcessWhenTestServerPortIsZeroAndManagementPortIsNotNullAndDifferentInProduction() {
117+
addTestPropertySource("0", null);
118+
Map<String, Object> other = new HashMap<>();
119+
other.put("management.server.port", "8081");
120+
MapPropertySource otherSource = new MapPropertySource("other", other);
121+
this.propertySources.addLast(otherSource);
122+
this.postProcessor.postProcessEnvironment(this.environment, null);
123+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
124+
assertThat(this.environment.getProperty("management.server.port")).isEqualTo("0");
125+
}
126+
127+
@Test
128+
public void postProcessWhenTestServerPortIsZeroAndManagementPortMinusOne() {
129+
addTestPropertySource("0", null);
130+
Map<String, Object> other = new HashMap<>();
131+
other.put("management.server.port", "-1");
132+
MapPropertySource otherSource = new MapPropertySource("other", other);
133+
this.propertySources.addLast(otherSource);
134+
this.postProcessor.postProcessEnvironment(this.environment, null);
135+
assertThat(this.environment.getProperty("server.port")).isEqualTo("0");
136+
assertThat(this.environment.getProperty("management.server.port"))
137+
.isEqualTo("-1");
138+
}
139+
140+
private void addTestPropertySource(String serverPort, String managementPort) {
141+
Map<String, Object> source = new HashMap<>();
142+
source.put("server.port", serverPort);
143+
source.put("management.server.port", managementPort);
144+
MapPropertySource inlineTestSource = new MapPropertySource(
145+
TestPropertySourceUtils.INLINED_PROPERTIES_PROPERTY_SOURCE_NAME, source);
146+
this.propertySources.addFirst(inlineTestSource);
147+
}
148+
149+
}

0 commit comments

Comments
 (0)