diff --git a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
index 46460041fe3..01bebbc5ac8 100644
--- a/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
+++ b/spring-test/src/main/java/org/springframework/test/context/support/TestPropertySourceUtils.java
@@ -373,12 +373,11 @@ public abstract class TestPropertySourceUtils {
*
Parsing of the key-value pairs is achieved by converting all supplied
* strings into virtual properties files in memory and delegating to
* {@link Properties#load(java.io.Reader)} to parse each virtual file.
- *
Generally speaking, the ordering of property names will be preserved in
- * the returned map, analogous to the order in which the key-value pairs are
- * supplied to this method. However, if a single string contains multiple
- * key-value pairs separated by newlines — for example, when supplied by
- * a user via a text block — the ordering of property names for
- * those particular key-value pairs cannot be guaranteed in the returned map.
+ *
The ordering of property names will be preserved in the returned map,
+ * analogous to the order in which the key-value pairs are supplied to this
+ * method. This also applies if a single string contains multiple key-value
+ * pairs separated by newlines — for example, when supplied by a user
+ * via a text block.
*
For a full discussion of inlined properties, consult the Javadoc
* for {@link TestPropertySource#properties}.
* @param inlinedProperties the inlined properties to convert; potentially empty
@@ -390,26 +389,21 @@ public abstract class TestPropertySourceUtils {
*/
public static Map convertInlinedPropertiesToMap(String... inlinedProperties) {
Assert.notNull(inlinedProperties, "'inlinedProperties' must not be null");
- Map map = new LinkedHashMap<>();
- Properties props = new Properties();
+ SequencedProperties sequencedProperties = new SequencedProperties();
- for (String pair : inlinedProperties) {
- if (!StringUtils.hasText(pair)) {
+ for (String input : inlinedProperties) {
+ if (!StringUtils.hasText(input)) {
continue;
}
try {
- props.load(new StringReader(pair));
+ sequencedProperties.load(new StringReader(input));
}
catch (Exception ex) {
- throw new IllegalStateException("Failed to load test environment properties from [" + pair + "]", ex);
+ throw new IllegalStateException("Failed to load test environment properties from [" + input + "]", ex);
}
- for (String name : props.stringPropertyNames()) {
- map.put(name, props.getProperty(name));
- }
- props.clear();
}
- return map;
+ return sequencedProperties.map;
}
private static List>> findRepeatableAnnotations(
@@ -456,4 +450,24 @@ public abstract class TestPropertySourceUtils {
return Comparator.> comparingInt(MergedAnnotation::getDistance).reversed();
}
+ /**
+ * Extension of {@link Properties} that mimics a {@code SequencedMap} by
+ * tracking all added properties in a {@link LinkedHashMap} that can be
+ * accessed directly via the {@code map} field.
+ * @since 6.1
+ */
+ @SuppressWarnings("serial")
+ private static class SequencedProperties extends Properties {
+
+ final Map map = new LinkedHashMap<>();
+
+ @Override
+ public synchronized Object put(Object key, Object value) {
+ if (key instanceof String str) {
+ this.map.put(str, value);
+ }
+ return super.put(key, value);
+ }
+ }
+
}
diff --git a/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesWithTextBlockTestPropertySourceTests.java b/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesWithTextBlockTestPropertySourceTests.java
index 95bc931bcd4..ef35896b03a 100644
--- a/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesWithTextBlockTestPropertySourceTests.java
+++ b/spring-test/src/test/java/org/springframework/test/context/env/InlinedPropertiesWithTextBlockTestPropertySourceTests.java
@@ -78,16 +78,12 @@ class InlinedPropertiesWithTextBlockTestPropertySourceTests {
assertEnvironmentProperty(this.env, "key.value.3", "key:value");
}
- /**
- * Not necessarily preserved because the properties are all added at the
- * same time.
- */
@Test
@SuppressWarnings("rawtypes")
- void propertyNameOrderingIsNotNecessarilyPreservedInEnvironment() {
+ void propertyNameOrderingIsPreservedInEnvironment() {
EnumerablePropertySource eps = (EnumerablePropertySource) env.getPropertySources().get(
INLINED_PROPERTIES_PROPERTY_SOURCE_NAME);
- assertThat(eps.getPropertyNames()).containsExactlyInAnyOrder("foo", "baz", "enigma", "x.y.z",
+ assertThat(eps.getPropertyNames()).containsExactly("foo", "baz", "enigma", "x.y.z",
"server.url", "key.value.1", "key.value.2", "key.value.3");
}