|
|
|
@ -46,30 +46,28 @@ import org.springframework.test.context.support.AbstractContextLoader; |
|
|
|
import org.springframework.test.context.support.AnnotationConfigContextLoaderUtils; |
|
|
|
import org.springframework.test.context.support.AnnotationConfigContextLoaderUtils; |
|
|
|
import org.springframework.test.context.web.WebAppConfiguration; |
|
|
|
import org.springframework.test.context.web.WebAppConfiguration; |
|
|
|
import org.springframework.test.context.web.WebMergedContextConfiguration; |
|
|
|
import org.springframework.test.context.web.WebMergedContextConfiguration; |
|
|
|
|
|
|
|
import org.springframework.util.Assert; |
|
|
|
import org.springframework.util.ObjectUtils; |
|
|
|
import org.springframework.util.ObjectUtils; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.util.StringUtils; |
|
|
|
import org.springframework.web.context.support.GenericWebApplicationContext; |
|
|
|
import org.springframework.web.context.support.GenericWebApplicationContext; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* A {@link ContextLoader} that can be used to test Spring Boot applications (those that |
|
|
|
* A {@link ContextLoader} that can be used to test Spring Boot applications (those that |
|
|
|
* normally startup using {@link SpringApplication}). Normally never starts an embedded |
|
|
|
* normally startup using {@link SpringApplication}). Can be used to test non-web features |
|
|
|
* web server, but detects the {@link WebAppConfiguration @WebAppConfiguration} annotation |
|
|
|
* (like a repository layer) or start an fully-configured embedded servlet container. |
|
|
|
* on the test class and only creates a web application context if it is present. Non-web |
|
|
|
|
|
|
|
* features, like a repository layer, can be tested cleanly by simply <em>not</em> marking |
|
|
|
|
|
|
|
* the test class <code>@WebAppConfiguration</code>. |
|
|
|
|
|
|
|
* <p> |
|
|
|
* <p> |
|
|
|
* If you <em>want</em> to start a web server, mark the test class as |
|
|
|
* Use {@code @WebIntegrationTest} (or {@code @IntegrationTest} with |
|
|
|
* <code>@WebAppConfiguration @IntegrationTest</code>. This is useful for testing HTTP |
|
|
|
* {@code @WebAppConfiguration}) to indicate that you want to use a real servlet container |
|
|
|
* endpoints using {@link TestRestTemplate} (for instance), especially since you can |
|
|
|
* or {@code @WebAppConfiguration} alone to use a {@link MockServletContext}. |
|
|
|
* <code>@Autowired</code> application context components into your test case to see the |
|
|
|
|
|
|
|
* internal effects of HTTP requests directly. |
|
|
|
|
|
|
|
* <p> |
|
|
|
* <p> |
|
|
|
* If <code>@ActiveProfiles</code> are provided in the test class they will be used to |
|
|
|
* If <code>@ActiveProfiles</code> are provided in the test class they will be used to |
|
|
|
* create the application context. |
|
|
|
* create the application context. |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Dave Syer |
|
|
|
* @author Dave Syer |
|
|
|
|
|
|
|
* @author Phillip Webb |
|
|
|
* @see IntegrationTest |
|
|
|
* @see IntegrationTest |
|
|
|
* @see WebIntegrationTest |
|
|
|
* @see WebIntegrationTest |
|
|
|
|
|
|
|
* @see TestRestTemplate |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
|
|
|
|
|
|
|
|
@ -78,21 +76,15 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public ApplicationContext loadContext(MergedContextConfiguration config) |
|
|
|
public ApplicationContext loadContext(MergedContextConfiguration config) |
|
|
|
throws Exception { |
|
|
|
throws Exception { |
|
|
|
|
|
|
|
assertValidAnnotations(config.getTestClass()); |
|
|
|
SpringApplication application = getSpringApplication(); |
|
|
|
SpringApplication application = getSpringApplication(); |
|
|
|
application.setSources(getSources(config)); |
|
|
|
application.setSources(getSources(config)); |
|
|
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
|
|
|
ConfigurableEnvironment environment = new StandardEnvironment(); |
|
|
|
if (!ObjectUtils.isEmpty(config.getActiveProfiles())) { |
|
|
|
if (!ObjectUtils.isEmpty(config.getActiveProfiles())) { |
|
|
|
String profiles = StringUtils.arrayToCommaDelimitedString(config |
|
|
|
setActiveProfiles(environment, config.getActiveProfiles()); |
|
|
|
.getActiveProfiles()); |
|
|
|
|
|
|
|
EnvironmentTestUtils.addEnvironment(environment, "spring.profiles.active=" |
|
|
|
|
|
|
|
+ profiles); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
// Ensure @IntegrationTest properties go before external config and after system
|
|
|
|
Map<String, Object> properties = getEnvironmentProperties(config); |
|
|
|
environment.getPropertySources() |
|
|
|
addProperties(environment, properties); |
|
|
|
.addAfter( |
|
|
|
|
|
|
|
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, |
|
|
|
|
|
|
|
new MapPropertySource("integrationTest", |
|
|
|
|
|
|
|
getEnvironmentProperties(config))); |
|
|
|
|
|
|
|
application.setEnvironment(environment); |
|
|
|
application.setEnvironment(environment); |
|
|
|
List<ApplicationContextInitializer<?>> initializers = getInitializers(config, |
|
|
|
List<ApplicationContextInitializer<?>> initializers = getInitializers(config, |
|
|
|
application); |
|
|
|
application); |
|
|
|
@ -106,13 +98,14 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
return application.run(); |
|
|
|
return application.run(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
private void assertValidAnnotations(Class<?> testClass) { |
|
|
|
public void processContextConfiguration( |
|
|
|
boolean hasWebAppConfiguration = AnnotationUtils.findAnnotation(testClass, |
|
|
|
ContextConfigurationAttributes configAttributes) { |
|
|
|
WebAppConfiguration.class) != null; |
|
|
|
if (!configAttributes.hasLocations() && !configAttributes.hasClasses()) { |
|
|
|
boolean hasWebIntegrationTest = AnnotationUtils.findAnnotation(testClass, |
|
|
|
Class<?>[] defaultConfigClasses = detectDefaultConfigurationClasses(configAttributes |
|
|
|
WebIntegrationTest.class) != null; |
|
|
|
.getDeclaringClass()); |
|
|
|
if (hasWebAppConfiguration && hasWebIntegrationTest) { |
|
|
|
configAttributes.setClasses(defaultConfigClasses); |
|
|
|
throw new IllegalStateException("@WebIntegrationTest and " |
|
|
|
|
|
|
|
+ "@WebAppConfiguration cannot be used together"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -129,27 +122,16 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
Set<Object> sources = new LinkedHashSet<Object>(); |
|
|
|
Set<Object> sources = new LinkedHashSet<Object>(); |
|
|
|
sources.addAll(Arrays.asList(mergedConfig.getClasses())); |
|
|
|
sources.addAll(Arrays.asList(mergedConfig.getClasses())); |
|
|
|
sources.addAll(Arrays.asList(mergedConfig.getLocations())); |
|
|
|
sources.addAll(Arrays.asList(mergedConfig.getLocations())); |
|
|
|
if (sources.isEmpty()) { |
|
|
|
Assert.state(sources.size() > 0, "No configuration classes " |
|
|
|
throw new IllegalStateException( |
|
|
|
+ "or locations found in @SpringApplicationConfiguration. " |
|
|
|
"No configuration classes or locations found in @SpringApplicationConfiguration. " |
|
|
|
+ "For default configuration detection to work you need " |
|
|
|
+ "For default configuration detection to work you need Spring 4.0.3 or better (found " |
|
|
|
+ "Spring 4.0.3 or better (found " + SpringVersion.getVersion() + ")."); |
|
|
|
+ SpringVersion.getVersion() + ")."); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return sources; |
|
|
|
return sources; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
private void setActiveProfiles(ConfigurableEnvironment environment, String[] profiles) { |
|
|
|
* Detect the default configuration classes for the supplied test class. By default |
|
|
|
EnvironmentTestUtils.addEnvironment(environment, "spring.profiles.active=" |
|
|
|
* simply delegates to |
|
|
|
+ StringUtils.arrayToCommaDelimitedString(profiles)); |
|
|
|
* {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses} . |
|
|
|
|
|
|
|
* @param declaringClass the test class that declared {@code @ContextConfiguration} |
|
|
|
|
|
|
|
* @return an array of default configuration classes, potentially empty but never |
|
|
|
|
|
|
|
* {@code null} |
|
|
|
|
|
|
|
* @see AnnotationConfigContextLoaderUtils |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) { |
|
|
|
|
|
|
|
return AnnotationConfigContextLoaderUtils |
|
|
|
|
|
|
|
.detectDefaultConfigurationClasses(declaringClass); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
protected Map<String, Object> getEnvironmentProperties( |
|
|
|
protected Map<String, Object> getEnvironmentProperties( |
|
|
|
@ -159,8 +141,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
disableJmx(properties); |
|
|
|
disableJmx(properties); |
|
|
|
properties.putAll(extractEnvironmentProperties(config |
|
|
|
properties.putAll(extractEnvironmentProperties(config |
|
|
|
.getPropertySourceProperties())); |
|
|
|
.getPropertySourceProperties())); |
|
|
|
if (!isAnnotated(config.getTestClass(), IntegrationTest.class, |
|
|
|
if (!isIntegrationTest(config.getTestClass())) { |
|
|
|
WebIntegrationTest.class)) { |
|
|
|
|
|
|
|
properties.putAll(getDefaultEnvironmentProperties()); |
|
|
|
properties.putAll(getDefaultEnvironmentProperties()); |
|
|
|
} |
|
|
|
} |
|
|
|
return properties; |
|
|
|
return properties; |
|
|
|
@ -170,11 +151,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
properties.put("spring.jmx.enabled", "false"); |
|
|
|
properties.put("spring.jmx.enabled", "false"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private Map<String, String> getDefaultEnvironmentProperties() { |
|
|
|
final Map<String, Object> extractEnvironmentProperties(String[] values) { |
|
|
|
return Collections.singletonMap("server.port", "-1"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Map<String, Object> extractEnvironmentProperties(String[] values) { |
|
|
|
|
|
|
|
// Instead of parsing the keys ourselves, we rely on standard handling
|
|
|
|
// Instead of parsing the keys ourselves, we rely on standard handling
|
|
|
|
if (values == null) { |
|
|
|
if (values == null) { |
|
|
|
return Collections.emptyMap(); |
|
|
|
return Collections.emptyMap(); |
|
|
|
@ -199,6 +176,18 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
return map; |
|
|
|
return map; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private Map<String, String> getDefaultEnvironmentProperties() { |
|
|
|
|
|
|
|
return Collections.singletonMap("server.port", "-1"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void addProperties(ConfigurableEnvironment environment, |
|
|
|
|
|
|
|
Map<String, Object> properties) { |
|
|
|
|
|
|
|
// @IntegrationTest properties go before external configuration and after system
|
|
|
|
|
|
|
|
environment.getPropertySources().addAfter( |
|
|
|
|
|
|
|
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, |
|
|
|
|
|
|
|
new MapPropertySource("integrationTest", properties)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private List<ApplicationContextInitializer<?>> getInitializers( |
|
|
|
private List<ApplicationContextInitializer<?>> getInitializers( |
|
|
|
MergedContextConfiguration mergedConfig, SpringApplication application) { |
|
|
|
MergedContextConfiguration mergedConfig, SpringApplication application) { |
|
|
|
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(); |
|
|
|
List<ApplicationContextInitializer<?>> initializers = new ArrayList<ApplicationContextInitializer<?>>(); |
|
|
|
@ -211,10 +200,34 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
return initializers; |
|
|
|
return initializers; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
public void processContextConfiguration( |
|
|
|
|
|
|
|
ContextConfigurationAttributes configAttributes) { |
|
|
|
|
|
|
|
if (!configAttributes.hasLocations() && !configAttributes.hasClasses()) { |
|
|
|
|
|
|
|
Class<?>[] defaultConfigClasses = detectDefaultConfigurationClasses(configAttributes |
|
|
|
|
|
|
|
.getDeclaringClass()); |
|
|
|
|
|
|
|
configAttributes.setClasses(defaultConfigClasses); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Detect the default configuration classes for the supplied test class. By default |
|
|
|
|
|
|
|
* simply delegates to |
|
|
|
|
|
|
|
* {@link AnnotationConfigContextLoaderUtils#detectDefaultConfigurationClasses} . |
|
|
|
|
|
|
|
* @param declaringClass the test class that declared {@code @ContextConfiguration} |
|
|
|
|
|
|
|
* @return an array of default configuration classes, potentially empty but never |
|
|
|
|
|
|
|
* {@code null} |
|
|
|
|
|
|
|
* @see AnnotationConfigContextLoaderUtils |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
protected Class<?>[] detectDefaultConfigurationClasses(Class<?> declaringClass) { |
|
|
|
|
|
|
|
return AnnotationConfigContextLoaderUtils |
|
|
|
|
|
|
|
.detectDefaultConfigurationClasses(declaringClass); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
public ApplicationContext loadContext(String... locations) throws Exception { |
|
|
|
public ApplicationContext loadContext(String... locations) throws Exception { |
|
|
|
throw new UnsupportedOperationException( |
|
|
|
throw new UnsupportedOperationException("SpringApplicationContextLoader " |
|
|
|
"SpringApplicationContextLoader does not support the loadContext(String...) method"); |
|
|
|
+ "does not support the loadContext(String...) method"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@Override |
|
|
|
@ -222,33 +235,37 @@ public class SpringApplicationContextLoader extends AbstractContextLoader { |
|
|
|
return "-context.xml"; |
|
|
|
return "-context.xml"; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Inner class to configure {@link WebMergedContextConfiguration}. |
|
|
|
|
|
|
|
*/ |
|
|
|
private static class WebConfigurer { |
|
|
|
private static class WebConfigurer { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static final Class<GenericWebApplicationContext> WEB_CONTEXT_CLASS = GenericWebApplicationContext.class; |
|
|
|
|
|
|
|
|
|
|
|
void configure(MergedContextConfiguration configuration, |
|
|
|
void configure(MergedContextConfiguration configuration, |
|
|
|
SpringApplication application, |
|
|
|
SpringApplication application, |
|
|
|
List<ApplicationContextInitializer<?>> initializers) { |
|
|
|
List<ApplicationContextInitializer<?>> initializers) { |
|
|
|
WebMergedContextConfiguration webConfiguration = (WebMergedContextConfiguration) configuration; |
|
|
|
WebMergedContextConfiguration webConfiguration = (WebMergedContextConfiguration) configuration; |
|
|
|
if (!isAnnotated(webConfiguration.getTestClass(), IntegrationTest.class, |
|
|
|
if (!isIntegrationTest(webConfiguration.getTestClass())) { |
|
|
|
WebIntegrationTest.class)) { |
|
|
|
addMockServletContext(initializers, webConfiguration); |
|
|
|
MockServletContext servletContext = new MockServletContext( |
|
|
|
application.setApplicationContextClass(WEB_CONTEXT_CLASS); |
|
|
|
webConfiguration.getResourceBasePath()); |
|
|
|
|
|
|
|
initializers.add(0, new ServletContextApplicationContextInitializer( |
|
|
|
|
|
|
|
servletContext)); |
|
|
|
|
|
|
|
application |
|
|
|
|
|
|
|
.setApplicationContextClass(GenericWebApplicationContext.class); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void addMockServletContext( |
|
|
|
|
|
|
|
List<ApplicationContextInitializer<?>> initializers, |
|
|
|
|
|
|
|
WebMergedContextConfiguration webConfiguration) { |
|
|
|
|
|
|
|
MockServletContext servletContext = new MockServletContext( |
|
|
|
|
|
|
|
webConfiguration.getResourceBasePath()); |
|
|
|
|
|
|
|
initializers.add(0, new ServletContextApplicationContextInitializer( |
|
|
|
|
|
|
|
servletContext)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings({ "unchecked", "rawtypes" }) |
|
|
|
private static boolean isIntegrationTest(Class<?> testClass) { |
|
|
|
private static boolean isAnnotated(Class<?> testClass, Class<?>... annotations) { |
|
|
|
return ((AnnotationUtils.findAnnotation(testClass, IntegrationTest.class) != null) || (AnnotationUtils |
|
|
|
for (Class<?> annotation : annotations) { |
|
|
|
.findAnnotation(testClass, WebIntegrationTest.class) != null)); |
|
|
|
if (AnnotationUtils.findAnnotation(testClass, (Class) annotation) != null) { |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|