Merge branch '2.7.x'

This commit is contained in:
Phillip Webb 2022-01-14 13:57:35 -08:00
commit cb97aff1e7
3 changed files with 88 additions and 19 deletions

View File

@ -18,12 +18,15 @@ package org.springframework.boot.test.context;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.boot.ApplicationContextFactory; import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.DefaultPropertiesPropertySource;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType; import org.springframework.boot.WebApplicationType;
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.mock.web.SpringBootMockServletContext; import org.springframework.boot.test.mock.web.SpringBootMockServletContext;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
@ -32,14 +35,18 @@ import org.springframework.boot.web.reactive.context.GenericReactiveWebApplicati
import org.springframework.boot.web.servlet.support.ServletContextApplicationContextInitializer; import org.springframework.boot.web.servlet.support.ServletContextApplicationContextInitializer;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.SpringVersion; import org.springframework.core.SpringVersion;
import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.env.CommandLinePropertySource;
import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment; import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader; import org.springframework.core.io.ResourceLoader;
@ -53,6 +60,7 @@ import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.context.web.WebMergedContextConfiguration; import org.springframework.test.context.web.WebMergedContextConfiguration;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.context.support.GenericWebApplicationContext;
@ -81,6 +89,9 @@ import org.springframework.web.context.support.GenericWebApplicationContext;
*/ */
public class SpringBootContextLoader extends AbstractContextLoader { public class SpringBootContextLoader extends AbstractContextLoader {
private static final String[] PRIORITY_PROPERTY_SOURCES = { "configurationProperties",
DefaultPropertiesPropertySource.NAME, CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME };
@Override @Override
public ApplicationContext loadContext(MergedContextConfiguration config) throws Exception { public ApplicationContext loadContext(MergedContextConfiguration config) throws Exception {
Class<?>[] configClasses = config.getClasses(); Class<?>[] configClasses = config.getClasses();
@ -111,23 +122,49 @@ public class SpringBootContextLoader extends AbstractContextLoader {
application.setWebApplicationType(WebApplicationType.NONE); application.setWebApplicationType(WebApplicationType.NONE);
} }
application.setInitializers(initializers); application.setInitializers(initializers);
ConfigurableEnvironment environment = getEnvironment(); boolean customEnvironent = ReflectionUtils.findMethod(getClass(), "getEnvironment")
setActiveProfiles(environment, config.getActiveProfiles()); .getDeclaringClass() != SpringBootContextLoader.class;
if (customEnvironent) {
ConfigurableEnvironment environment = getEnvironment();
prepareEnvironment(config, application, environment, false);
application.setEnvironment(environment);
}
else {
application.addListeners(new PrepareEnvironmentListener(config));
}
String[] args = SpringBootTestArgs.get(config.getContextCustomizers());
return application.run(args);
}
private void prepareEnvironment(MergedContextConfiguration config, SpringApplication application,
ConfigurableEnvironment environment, boolean applicationEnvironment) {
MutablePropertySources propertySources = environment.getPropertySources();
List<PropertySource<?>> priorityPropertySources = new ArrayList<>();
if (applicationEnvironment) {
for (String priorityPropertySourceName : PRIORITY_PROPERTY_SOURCES) {
PropertySource<?> priorityPropertySource = propertySources.get(priorityPropertySourceName);
if (priorityPropertySource != null) {
priorityPropertySources.add(priorityPropertySource);
propertySources.remove(priorityPropertySourceName);
}
}
}
setActiveProfiles(environment, config.getActiveProfiles(), applicationEnvironment);
ResourceLoader resourceLoader = (application.getResourceLoader() != null) ? application.getResourceLoader() ResourceLoader resourceLoader = (application.getResourceLoader() != null) ? application.getResourceLoader()
: new DefaultResourceLoader(null); : new DefaultResourceLoader(null);
TestPropertySourceUtils.addPropertiesFilesToEnvironment(environment, resourceLoader, TestPropertySourceUtils.addPropertiesFilesToEnvironment(environment, resourceLoader,
config.getPropertySourceLocations()); config.getPropertySourceLocations());
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, getInlinedProperties(config)); TestPropertySourceUtils.addInlinedPropertiesToEnvironment(environment, getInlinedProperties(config));
application.setEnvironment(environment); Collections.reverse(priorityPropertySources);
String[] args = SpringBootTestArgs.get(config.getContextCustomizers()); priorityPropertySources.forEach(propertySources::addFirst);
return application.run(args);
} }
private void setActiveProfiles(ConfigurableEnvironment environment, String[] profiles) { private void setActiveProfiles(ConfigurableEnvironment environment, String[] profiles,
boolean applicationEnvironment) {
if (ObjectUtils.isEmpty(profiles)) { if (ObjectUtils.isEmpty(profiles)) {
return; return;
} }
if (!(environment instanceof TestEnvironment)) { if (!applicationEnvironment) {
environment.setActiveProfiles(profiles); environment.setActiveProfiles(profiles);
} }
String[] pairs = new String[profiles.length]; String[] pairs = new String[profiles.length];
@ -152,7 +189,7 @@ public class SpringBootContextLoader extends AbstractContextLoader {
* @return a {@link ConfigurableEnvironment} instance * @return a {@link ConfigurableEnvironment} instance
*/ */
protected ConfigurableEnvironment getEnvironment() { protected ConfigurableEnvironment getEnvironment() {
return new TestEnvironment(); return new StandardEnvironment();
} }
protected String[] getInlinedProperties(MergedContextConfiguration config) { protected String[] getInlinedProperties(MergedContextConfiguration config) {
@ -304,19 +341,25 @@ public class SpringBootContextLoader extends AbstractContextLoader {
} }
/** /**
* Side-effect free {@link Environment} that doesn't set profiles directly from * {@link ApplicationListener} used to prepare the application created environment.
* properties.
*/ */
static class TestEnvironment extends StandardEnvironment { private class PrepareEnvironmentListener
implements ApplicationListener<ApplicationEnvironmentPreparedEvent>, PriorityOrdered {
@Override private final MergedContextConfiguration config;
protected String doGetActiveProfilesProperty() {
return null; PrepareEnvironmentListener(MergedContextConfiguration config) {
this.config = config;
} }
@Override @Override
protected String doGetDefaultProfilesProperty() { public int getOrder() {
return null; return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
prepareEnvironment(this.config, event.getSpringApplication(), event.getEnvironment(), true);
} }
} }

View File

@ -16,7 +16,9 @@
package org.springframework.boot.test.context; package org.springframework.boot.test.context;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -24,11 +26,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment; import org.springframework.core.env.StandardEnvironment;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.MergedContextConfiguration; import org.springframework.test.context.MergedContextConfiguration;
import org.springframework.test.context.TestContext; import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestContextManager; import org.springframework.test.context.TestContextManager;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
@ -127,6 +132,20 @@ class SpringBootContextLoaderTests {
assertThat(environment.getPropertySources().get("active-test-profiles")).isNotNull(); assertThat(environment.getPropertySources().get("active-test-profiles")).isNotNull();
} }
@Test
void propertySourceOrdering() throws Exception {
TestContext context = new ExposedTestContextManager(PropertySourceOrdering.class).getExposedTestContext();
ConfigurableEnvironment environment = (ConfigurableEnvironment) context.getApplicationContext()
.getEnvironment();
List<String> names = environment.getPropertySources().stream().map(PropertySource::getName)
.collect(Collectors.toList());
String last = names.remove(names.size() - 1);
assertThat(names).containsExactly("configurationProperties", "commandLineArgs", "Inlined Test Properties",
"servletConfigInitParams", "servletContextInitParams", "systemProperties", "systemEnvironment",
"random");
assertThat(last).startsWith("Config resource");
}
private String[] getActiveProfiles(Class<?> testClass) { private String[] getActiveProfiles(Class<?> testClass) {
TestContext testContext = new ExposedTestContextManager(testClass).getExposedTestContext(); TestContext testContext = new ExposedTestContextManager(testClass).getExposedTestContext();
ApplicationContext applicationContext = testContext.getApplicationContext(); ApplicationContext applicationContext = testContext.getApplicationContext();
@ -198,6 +217,12 @@ class SpringBootContextLoaderTests {
} }
@SpringBootTest(classes = Config.class, args = "args", properties = "one=1")
@TestPropertySource(properties = "two=2")
static class PropertySourceOrdering {
}
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
static class Config { static class Config {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2021 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
@ -32,7 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* *
* @author Madhura Bhave * @author Madhura Bhave
*/ */
@SpringBootTest(properties = "enableEnvironmentPostProcessor=true") // gh-28530 @SpringBootTest(webEnvironment = WebEnvironment.NONE, properties = "enableEnvironmentPostProcessor=true") // gh-28530
@ActiveProfiles("hello") @ActiveProfiles("hello")
class ActiveProfilesTests { class ActiveProfilesTests {