Skip to content

Commit 64270ec

Browse files
committed
Convert environment used by SpringBootTestContextLoader
This commit aligns `SpringBootTest`s to also use `ApplicationEnvironment` instead of `StandardEnvironment`. This prevents the side-effect of active profiles from `@ActiveProfiles` from being added to the environment when doGetActiveProfiles is called. In this case, calling `addActiveProfiles()` in the environment post processor would result in `@ActiveProfiles` being added to the environment first, resulting in the wrong order. The additional call to `setActiveProfiles()` is also not necessary when using ApplicationEnvironment because that call was put in place to prevent the side-effect which `ApplicationEnvironment` does not have. Fixes gh-28530
1 parent 7fbb9d4 commit 64270ec

File tree

6 files changed

+197
-22
lines changed

6 files changed

+197
-22
lines changed

Diff for: spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootContextLoader.java

+30-20
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,6 @@ public ApplicationContext loadContext(MergedContextConfiguration config) throws
9191
application.setMainApplicationClass(config.getTestClass());
9292
application.addPrimarySources(Arrays.asList(configClasses));
9393
application.getSources().addAll(Arrays.asList(configLocations));
94-
ConfigurableEnvironment environment = getEnvironment();
95-
if (!ObjectUtils.isEmpty(config.getActiveProfiles())) {
96-
setActiveProfiles(environment, config.getActiveProfiles());
97-
}
98-
ResourceLoader resourceLoader = (application.getResourceLoader() != null) ? application.getResourceLoader()
99-
: new DefaultResourceLoader(null);
100-
TestPropertySourceUtils.addPropertiesFilesToEnvironment(environment, resourceLoader,
101-
config.getPropertySourceLocations());
102-
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, getInlinedProperties(config));
103-
application.setEnvironment(environment);
10494
List<ApplicationContextInitializer<?>> initializers = getInitializers(config, application);
10595
if (config instanceof WebMergedContextConfiguration) {
10696
application.setWebApplicationType(WebApplicationType.SERVLET);
@@ -119,10 +109,40 @@ else if (config instanceof ReactiveWebMergedContextConfiguration) {
119109
application.setWebApplicationType(WebApplicationType.NONE);
120110
}
121111
application.setInitializers(initializers);
112+
ConfigurableEnvironment environment = getEnvironment(application, config.getActiveProfiles());
113+
ResourceLoader resourceLoader = (application.getResourceLoader() != null) ? application.getResourceLoader()
114+
: new DefaultResourceLoader(null);
115+
TestPropertySourceUtils.addPropertiesFilesToEnvironment(environment, resourceLoader,
116+
config.getPropertySourceLocations());
117+
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, getInlinedProperties(config));
118+
application.setEnvironment(environment);
122119
String[] args = SpringBootTestArgs.get(config.getContextCustomizers());
123120
return application.run(args);
124121
}
125122

123+
private ConfigurableEnvironment getEnvironment(SpringApplication application, String[] activeProfiles) {
124+
ConfigurableEnvironment environment = getEnvironment();
125+
boolean applicationEnvironment = false;
126+
if (environment.getClass() == StandardEnvironment.class) {
127+
environment = application.convertEnvironment(environment);
128+
applicationEnvironment = true;
129+
}
130+
setActiveProfiles(environment, activeProfiles, applicationEnvironment);
131+
return environment;
132+
}
133+
134+
private void setActiveProfiles(ConfigurableEnvironment environment, String[] profiles,
135+
boolean applicationEnvironment) {
136+
if (!applicationEnvironment) {
137+
environment.setActiveProfiles(profiles);
138+
}
139+
String[] pairs = new String[profiles.length];
140+
for (int i = 0; i < profiles.length; i++) {
141+
pairs[i] = "spring.profiles.active[" + i + "]=" + profiles[i];
142+
}
143+
TestPropertyValues.of(pairs).applyTo(environment);
144+
}
145+
126146
/**
127147
* Builds new {@link org.springframework.boot.SpringApplication} instance. You can
128148
* override this method to add custom behavior
@@ -141,16 +161,6 @@ protected ConfigurableEnvironment getEnvironment() {
141161
return new StandardEnvironment();
142162
}
143163

144-
private void setActiveProfiles(ConfigurableEnvironment environment, String[] profiles) {
145-
environment.setActiveProfiles(profiles);
146-
// Also add as properties to override any application.properties
147-
String[] pairs = new String[profiles.length];
148-
for (int i = 0; i < profiles.length; i++) {
149-
pairs[i] = "spring.profiles.active[" + i + "]=" + profiles[i];
150-
}
151-
TestPropertyValues.of(pairs).applyTo(environment);
152-
}
153-
154164
protected String[] getInlinedProperties(MergedContextConfiguration config) {
155165
ArrayList<String> properties = new ArrayList<>();
156166
// JMX bean names will clash if the same bean is used in multiple contexts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2012-2021 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+
* https://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+
17+
package org.springframework.boot.test.context;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.context.annotation.Configuration;
23+
import org.springframework.core.env.ConfigurableEnvironment;
24+
import org.springframework.core.env.Environment;
25+
import org.springframework.mock.env.MockEnvironment;
26+
import org.springframework.test.context.ActiveProfiles;
27+
import org.springframework.test.context.ContextConfiguration;
28+
29+
import static org.assertj.core.api.Assertions.assertThat;
30+
31+
/**
32+
* Integration tests for {@link SpringBootTest @SpringBootTest} with a custom
33+
* {@link Environment}.
34+
*
35+
* @author Madhura Bhave
36+
*/
37+
@SpringBootTest
38+
@ActiveProfiles({ "test1", "test2" })
39+
@ContextConfiguration(loader = SpringBootTestWithCustomEnvironmentTests.Loader.class)
40+
class SpringBootTestWithCustomEnvironmentTests {
41+
42+
@Autowired
43+
private Environment environment;
44+
45+
@Test
46+
void getActiveProfiles() {
47+
assertThat(this.environment).isInstanceOf(MockEnvironment.class);
48+
assertThat(this.environment.getActiveProfiles()).containsOnly("test1", "test2");
49+
}
50+
51+
@Configuration
52+
static class Config {
53+
54+
}
55+
56+
static class Loader extends SpringBootContextLoader {
57+
58+
@Override
59+
protected ConfigurableEnvironment getEnvironment() {
60+
return new MockEnvironment();
61+
}
62+
63+
}
64+
65+
}

Diff for: spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -377,13 +377,24 @@ private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners
377377
"Environment prefix cannot be set via properties.");
378378
bindToSpringApplication(environment);
379379
if (!this.isCustomEnvironment) {
380-
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
381-
deduceEnvironmentClass());
380+
environment = convertEnvironment(environment);
382381
}
383382
ConfigurationPropertySources.attach(environment);
384383
return environment;
385384
}
386385

386+
/**
387+
* Convert the given {@link ConfigurableEnvironment environment} to an application
388+
* environment that doesn't attempt to resolve profile properties directly.
389+
* @param environment the environment to convert
390+
* @return the converted environment
391+
* @since 2.5.7
392+
*/
393+
public StandardEnvironment convertEnvironment(ConfigurableEnvironment environment) {
394+
return new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
395+
deduceEnvironmentClass());
396+
}
397+
387398
private Class<? extends StandardEnvironment> deduceEnvironmentClass() {
388399
switch (this.webApplicationType) {
389400
case SERVLET:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2012-2021 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+
* https://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+
17+
package smoketest.profile;
18+
19+
import org.springframework.boot.SpringApplication;
20+
import org.springframework.boot.env.EnvironmentPostProcessor;
21+
import org.springframework.core.Ordered;
22+
import org.springframework.core.annotation.Order;
23+
import org.springframework.core.env.ConfigurableEnvironment;
24+
25+
/**
26+
* {@link EnvironmentPostProcessor} that adds an active profile.
27+
*
28+
* @author Madhura Bhave
29+
*/
30+
@Order(Ordered.HIGHEST_PRECEDENCE)
31+
class ActiveProfilesEnvironmentPostProcessor implements EnvironmentPostProcessor {
32+
33+
@Override
34+
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
35+
if (environment.getProperty("enableEnvironmentPostProcessor") != null) {
36+
environment.addActiveProfile("dev");
37+
}
38+
}
39+
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.boot.env.EnvironmentPostProcessor=\
2+
smoketest.profile.ActiveProfilesEnvironmentPostProcessor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2012-2021 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+
* https://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+
17+
package smoketest.profile;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.boot.env.EnvironmentPostProcessor;
23+
import org.springframework.boot.test.context.SpringBootTest;
24+
import org.springframework.core.env.Environment;
25+
import org.springframework.test.context.ActiveProfiles;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* Tests that profiles are activited in the correct order from an
31+
* {@link EnvironmentPostProcessor}.
32+
*
33+
* @author Madhura Bhave
34+
*/
35+
@SpringBootTest(properties = "enableEnvironmentPostProcessor=true") // gh-28530
36+
@ActiveProfiles("hello")
37+
class ActiveProfilesTests {
38+
39+
@Autowired
40+
private Environment environment;
41+
42+
@Test
43+
void activeProfileShouldTakePrecedenceOverProgrammaticallySetProfile() {
44+
assertThat(this.environment.getActiveProfiles()).containsExactly("dev", "hello");
45+
}
46+
47+
}

0 commit comments

Comments
 (0)