diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java
index a2bc94787df..804e6f714c1 100644
--- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java
+++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMvcIntegrationTests.java
@@ -58,8 +58,8 @@ import static org.junit.Assert.assertTrue;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
-@WebAppConfiguration
@IntegrationTest("server.port=0")
+@WebAppConfiguration
@DirtiesContext
public class EndpointMvcIntegrationTests {
diff --git a/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java b/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java
index 7d2d2afe6f0..7c3cd2cbbea 100644
--- a/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java
+++ b/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTest.java
@@ -25,6 +25,7 @@ import java.lang.annotation.Target;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestExecutionListeners;
+import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
@@ -41,15 +42,21 @@ import org.springframework.test.context.transaction.TransactionalTestExecutionLi
@Target(ElementType.TYPE)
// Leave out the ServletTestExecutionListener because it only deals with Mock* servlet
// stuff. A real embedded application will not need the mocks.
-@TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
+@TestExecutionListeners(listeners = { IntegrationTestPropertiesListener.class, DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
+@TestPropertySource
public @interface IntegrationTest {
+ /**
+ * Synonym for properties().
+ */
+ String[] value() default {};
+
/**
* Properties in form {@literal key=value} that should be added to the Spring
* {@link Environment} before the test runs.
*/
- String[] value() default {};
+ String[] properties() default {"server.port=-1", "spring.jmx.enabled=false"};
}
diff --git a/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTestPropertiesListener.java b/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTestPropertiesListener.java
new file mode 100644
index 00000000000..c5a8a67a50b
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/test/IntegrationTestPropertiesListener.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2013-2104 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.boot.test;
+
+import java.util.Arrays;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.test.context.MergedContextConfiguration;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.support.AbstractTestExecutionListener;
+import org.springframework.test.util.ReflectionTestUtils;
+
+/**
+ * Manipulate the TestContext to merge properties from @IntegrationTest value
+ * and properties attributes.
+ *
+ * @author Dave Syer
+ *
+ */
+public class IntegrationTestPropertiesListener extends AbstractTestExecutionListener {
+
+ private String[] defaultValues = (String[]) AnnotationUtils.getDefaultValue(
+ IntegrationTest.class, "properties");
+
+ @Override
+ public void prepareTestInstance(TestContext testContext) throws Exception {
+ MergedContextConfiguration config = null;
+ try {
+ // Here be hacks...
+ config = (MergedContextConfiguration) ReflectionTestUtils.getField(
+ testContext, "mergedContextConfiguration");
+ ReflectionTestUtils.setField(config, "propertySourceProperties",
+ getEnvironmentProperties(config));
+ }
+ catch (IllegalStateException e) {
+ throw e;
+ }
+ catch (Exception e) {
+ }
+ }
+
+ protected String[] getEnvironmentProperties(MergedContextConfiguration config) {
+ IntegrationTest annotation = AnnotationUtils.findAnnotation(
+ config.getTestClass(), IntegrationTest.class);
+ return mergeProperties(
+ getDefaultEnvironmentProperties(config.getPropertySourceProperties(),
+ annotation), getEnvironmentProperties(annotation));
+ }
+
+ private String[] getDefaultEnvironmentProperties(String[] original,
+ IntegrationTest annotation) {
+ String[] defaults = mergeProperties(original, defaultValues);
+ if (annotation == null || defaults.length == 0) {
+ // Without an @IntegrationTest we can assume the defaults are fine
+ return defaults;
+ }
+ // If @IntegrationTest is present we don't provide a default for the server.port
+ return filterPorts((String[]) AnnotationUtils.getDefaultValue(annotation,
+ "properties"));
+ }
+
+ private String[] filterPorts(String[] values) {
+
+ Set result = new LinkedHashSet();
+ for (String value : values) {
+ if (!value.contains(".port")) {
+ result.add(value);
+ }
+ }
+ return result.toArray(new String[0]);
+
+ }
+
+ private String[] getEnvironmentProperties(IntegrationTest annotation) {
+ if (annotation == null) {
+ return new String[0];
+ }
+ if (Arrays.asList(annotation.properties()).equals(Arrays.asList(defaultValues))) {
+ return annotation.value();
+ }
+ if (annotation.value().length == 0) {
+ return annotation.properties();
+ }
+ throw new IllegalStateException(
+ "Either properties or value can be provided but not both");
+ }
+
+ private String[] mergeProperties(String[] original, String[] extra) {
+ Set result = new LinkedHashSet();
+ for (String value : original) {
+ result.add(value);
+ }
+ for (String value : extra) {
+ result.add(value);
+ }
+ return result.toArray(new String[0]);
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java b/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java
index 7e957f84507..38976740f0a 100644
--- a/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java
+++ b/spring-boot/src/main/java/org/springframework/boot/test/SpringApplicationContextLoader.java
@@ -20,9 +20,7 @@ import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashMap;
-import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -92,7 +90,7 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
.addAfter(
StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
new MapPropertySource("integrationTest",
- getEnvironmentProperties(config)));
+ extractEnvironmentProperties(config.getPropertySourceProperties())));
application.setEnvironment(environment);
List> initializers = getInitializers(config,
application);
@@ -107,6 +105,32 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
return application.run();
}
+ // Instead of parsing the keys ourselves, we rely on standard handling
+ protected Map extractEnvironmentProperties(String[] values) {
+ Map properties = new HashMap();
+ if (values==null) {
+ return properties;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String value : values) {
+ sb.append(value).append(LINE_SEPARATOR);
+ }
+ String content = sb.toString();
+ Properties props = new Properties();
+ try {
+ props.load(new StringReader(content));
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Unexpected could not load properties from '"
+ + content + "'", e);
+ }
+
+ for (String name : props.stringPropertyNames()) {
+ properties.put(name, props.getProperty(name));
+ }
+ return properties;
+ }
+
@Override
public void processContextConfiguration(
ContextConfigurationAttributes configAttributes) {
@@ -152,55 +176,8 @@ public class SpringApplicationContextLoader extends AbstractContextLoader {
return AnnotationConfigContextLoaderUtils
.detectDefaultConfigurationClasses(declaringClass);
}
-
- protected Map getEnvironmentProperties(
- MergedContextConfiguration config) {
- Map properties = new LinkedHashMap();
- // JMX bean names will clash if the same bean is used in multiple contexts
- disableJmx(properties);
- IntegrationTest annotation = AnnotationUtils.findAnnotation(
- config.getTestClass(), IntegrationTest.class);
- properties.putAll(getEnvironmentProperties(annotation));
- return properties;
- }
-
- private void disableJmx(Map properties) {
- properties.put("spring.jmx.enabled", "false");
- }
-
- private Map getEnvironmentProperties(IntegrationTest annotation) {
- if (annotation == null) {
- return getDefaultEnvironmentProperties();
- }
- return extractEnvironmentProperties(annotation.value());
- }
-
- private Map getDefaultEnvironmentProperties() {
- return Collections.singletonMap("server.port", "-1");
- }
-
- // Instead of parsing the keys ourselves, we rely on standard handling
- private Map extractEnvironmentProperties(String[] values) {
- StringBuilder sb = new StringBuilder();
- for (String value : values) {
- sb.append(value).append(LINE_SEPARATOR);
- }
- String content = sb.toString();
- Properties props = new Properties();
- try {
- props.load(new StringReader(content));
- }
- catch (IOException e) {
- throw new IllegalStateException("Unexpected could not load properties from '"
- + content + "'", e);
- }
-
- Map properties = new HashMap();
- for (String name : props.stringPropertyNames()) {
- properties.put(name, props.getProperty(name));
- }
- return properties;
- }
+
+
private List> getInitializers(
MergedContextConfiguration mergedConfig, SpringApplication application) {
diff --git a/spring-boot/src/test/java/org/springframework/boot/AdhocTestSuite.java b/spring-boot/src/test/java/org/springframework/boot/AdhocTestSuite.java
index 30874b0f2bf..7499b13fba7 100644
--- a/spring-boot/src/test/java/org/springframework/boot/AdhocTestSuite.java
+++ b/spring-boot/src/test/java/org/springframework/boot/AdhocTestSuite.java
@@ -16,6 +16,7 @@
package org.springframework.boot;
+import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@@ -30,7 +31,7 @@ import org.springframework.boot.test.SpringApplicationConfigurationJmxTests;
@RunWith(Suite.class)
@SuiteClasses({ SpringApplicationConfigurationJmxTests.class,
SpringApplicationConfigurationDefaultConfigurationTests.class })
-// @Ignore
+@Ignore
public class AdhocTestSuite {
}
diff --git a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationConfigurationJmxTests.java b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationConfigurationJmxTests.java
index 7a5d3a2c41d..8a1df6fcf3d 100644
--- a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationConfigurationJmxTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationConfigurationJmxTests.java
@@ -16,6 +16,8 @@
package org.springframework.boot.test;
+import static org.junit.Assert.assertFalse;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
@@ -25,8 +27,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import static org.junit.Assert.assertFalse;
-
/**
* Tests for disabling JMX by default
*
@@ -34,6 +34,7 @@ import static org.junit.Assert.assertFalse;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Config.class)
+@IntegrationTest
public class SpringApplicationConfigurationJmxTests {
@Value("${spring.jmx.enabled}")
diff --git a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoaderTests.java b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoaderTests.java
index d2b7c107b3f..426bda8b14b 100644
--- a/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoaderTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/test/SpringApplicationContextLoaderTests.java
@@ -16,15 +16,16 @@
package org.springframework.boot.test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
import java.util.Map;
import org.junit.Test;
import org.springframework.test.context.MergedContextConfiguration;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
+import org.springframework.test.context.TestContext;
+import org.springframework.test.context.TestContextManager;
+import org.springframework.test.util.ReflectionTestUtils;
/**
* Tests for {@link SpringApplicationContextLoader}
@@ -36,30 +37,57 @@ public class SpringApplicationContextLoaderTests {
private final SpringApplicationContextLoader loader = new SpringApplicationContextLoader();
@Test
- public void environmentPropertiesSimple() {
+ public void environmentPropertiesSimple() throws Exception {
Map config = getEnvironmentProperties(SimpleConfig.class);
assertKey(config, "key", "myValue");
assertKey(config, "anotherKey", "anotherValue");
}
@Test
- public void environmentPropertiesSeparatorInValue() {
+ public void environmentPropertiesDefaults() throws Exception {
+ Map config = getEnvironmentProperties(SimpleConfig.class);
+ assertMissingKey(config, "server.port");
+ assertKey(config, "spring.jmx.enabled", "false");
+ }
+
+ @Test
+ public void environmentPropertiesOverrideDefaults() throws Exception {
+ Map config = getEnvironmentProperties(OverrideConfig.class);
+ assertKey(config, "server.port", "2345");
+ }
+
+ @Test(expected=IllegalStateException.class)
+ public void environmentPropertiesIllegal() throws Exception {
+ getEnvironmentProperties(IllegalConfig.class);
+ }
+
+ @Test
+ public void environmentPropertiesAppend() throws Exception {
+ Map config = getEnvironmentProperties(AppendConfig.class);
+ assertKey(config, "key", "myValue");
+ assertKey(config, "otherKey", "otherValue");
+ }
+
+ @Test
+ public void environmentPropertiesSeparatorInValue() throws Exception {
Map config = getEnvironmentProperties(SameSeparatorInValue.class);
assertKey(config, "key", "my=Value");
assertKey(config, "anotherKey", "another:Value");
}
@Test
- public void environmentPropertiesAnotherSeparatorInValue() {
+ public void environmentPropertiesAnotherSeparatorInValue() throws Exception {
Map config = getEnvironmentProperties(AnotherSeparatorInValue.class);
assertKey(config, "key", "my:Value");
assertKey(config, "anotherKey", "another=Value");
}
- private Map getEnvironmentProperties(Class> testClass) {
- MergedContextConfiguration configuration = mock(MergedContextConfiguration.class);
- doReturn(testClass).when(configuration).getTestClass();
- return this.loader.getEnvironmentProperties(configuration);
+ private Map getEnvironmentProperties(Class> testClass) throws Exception {
+ TestContext context = new ExposedTestContextManager(testClass).getExposedTestContext();
+ new IntegrationTestPropertiesListener().prepareTestInstance(context);
+ MergedContextConfiguration config = (MergedContextConfiguration) ReflectionTestUtils.getField(
+ context, "mergedContextConfiguration");
+ return this.loader.extractEnvironmentProperties(config.getPropertySourceProperties());
}
private void assertKey(Map actual, String key, Object value) {
@@ -67,10 +95,26 @@ public class SpringApplicationContextLoaderTests {
assertEquals(value, actual.get(key));
}
+ private void assertMissingKey(Map actual, String key) {
+ assertTrue("Key '" + key + "' found", !actual.containsKey(key));
+ }
+
@IntegrationTest({ "key=myValue", "anotherKey:anotherValue" })
static class SimpleConfig {
}
+ @IntegrationTest({ "server.port=2345" })
+ static class OverrideConfig {
+ }
+
+ @IntegrationTest(value = { "key=aValue", "anotherKey:anotherValue" }, properties = { "key=myValue", "otherKey=otherValue" })
+ static class IllegalConfig {
+ }
+
+ @IntegrationTest(properties = { "key=myValue", "otherKey=otherValue" })
+ static class AppendConfig {
+ }
+
@IntegrationTest({ "key=my=Value", "anotherKey:another:Value" })
static class SameSeparatorInValue {
}
@@ -78,5 +122,18 @@ public class SpringApplicationContextLoaderTests {
@IntegrationTest({ "key=my:Value", "anotherKey:another=Value" })
static class AnotherSeparatorInValue {
}
+
+ private static class ExposedTestContextManager extends TestContextManager {
+
+ public ExposedTestContextManager(Class> testClass) {
+ super(testClass);
+ }
+
+ public final TestContext getExposedTestContext() {
+ return super.getTestContext();
+ }
+
+
+ }
}