Allow TestPropertyValues.of to take a Map source

Extend the API of `TestPropertyValues` so that it can be constructed
from an existing `Map` or a `Stream` and mapping `Function`.

Closes gh-23685
This commit is contained in:
Phillip Webb 2020-10-23 11:55:48 -07:00
parent 7df18d9a91
commit e790828e19
2 changed files with 167 additions and 13 deletions

View File

@ -23,6 +23,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@ -59,17 +60,62 @@ public final class TestPropertyValues {
}
/**
* Builder method to add more properties.
* Return a new {@link TestPropertyValues} instance with additional entries.
* Name-value pairs can be specified with colon (":") or equals ("=") separators.
* @param pairs the property pairs to add
* @return a new {@link TestPropertyValues} instance
*/
public TestPropertyValues and(String... pairs) {
return and(Arrays.stream(pairs).map(Pair::parse));
return and(Arrays.stream(pairs), Pair::parse);
}
private TestPropertyValues and(Stream<Pair> pairs) {
/**
* Return a new {@link TestPropertyValues} instance with additional entries.
* Name-value pairs can be specified with colon (":") or equals ("=") separators.
* @param pairs the property pairs to add
* @return a new {@link TestPropertyValues} instance
* @since 2.4.0
*/
public TestPropertyValues and(Iterable<String> pairs) {
return (pairs != null) ? and(StreamSupport.stream(pairs.spliterator(), false)) : this;
}
/**
* Return a new {@link TestPropertyValues} instance with additional entries.
* Name-value pairs can be specified with colon (":") or equals ("=") separators.
* @param pairs the property pairs to add
* @return a new {@link TestPropertyValues} instance
* @since 2.4.0
*/
public TestPropertyValues and(Stream<String> pairs) {
return (pairs != null) ? and(pairs, Pair::parse) : this;
}
/**
* Return a new {@link TestPropertyValues} instance with additional entries.
* @param map the map of properties that need to be added to the environment
* @return a new {@link TestPropertyValues} instance
* @since 2.4.0
*/
public TestPropertyValues and(Map<String, String> map) {
return (map != null) ? and(map.entrySet().stream(), Pair::fromMapEntry) : this;
}
/**
* Return a new {@link TestPropertyValues} instance with additional entries.
* @param <T> the stream element type
* @param stream the elements that need to be added to the environment
* @param mapper a mapper function to convert an element from the stream into a
* {@link Pair}
* @return a new {@link TestPropertyValues} instance
* @since 2.4.0
*/
public <T> TestPropertyValues and(Stream<T> stream, Function<T, Pair> mapper) {
if (stream == null) {
return this;
}
Map<String, Object> properties = new LinkedHashMap<>(this.properties);
pairs.filter(Objects::nonNull).forEach((pair) -> pair.addTo(properties));
stream.map(mapper).filter(Objects::nonNull).forEach((pair) -> pair.addTo(properties));
return new TestPropertyValues(properties);
}
@ -174,10 +220,7 @@ public final class TestPropertyValues {
* @return the new instance
*/
public static TestPropertyValues of(Iterable<String> pairs) {
if (pairs == null) {
return empty();
}
return of(StreamSupport.stream(pairs.spliterator(), false));
return (pairs != null) ? of(StreamSupport.stream(pairs.spliterator(), false)) : empty();
}
/**
@ -189,10 +232,30 @@ public final class TestPropertyValues {
* @return the new instance
*/
public static TestPropertyValues of(Stream<String> pairs) {
if (pairs == null) {
return empty();
}
return empty().and(pairs.map(Pair::parse));
return (pairs != null) ? of(pairs, Pair::parse) : empty();
}
/**
* Return a new {@link TestPropertyValues} with the underlying map populated with the
* given map entries.
* @param map the map of properties that need to be added to the environment
* @return the new instance
*/
public static TestPropertyValues of(Map<String, String> map) {
return (map != null) ? of(map.entrySet().stream(), Pair::fromMapEntry) : empty();
}
/**
* Return a new {@link TestPropertyValues} with the underlying map populated with the
* given stream.
* @param <T> the stream element type
* @param stream the elements that need to be added to the environment
* @param mapper a mapper function to convert an element from the stream into a
* {@link Pair}
* @return the new instance
*/
public static <T> TestPropertyValues of(Stream<T> stream, Function<T, Pair> mapper) {
return (stream != null) ? empty().and(stream, mapper) : empty();
}
/**
@ -247,6 +310,13 @@ public final class TestPropertyValues {
private String value;
/**
* Create a new {@link Pair} instance.
* @param name the name
* @param value the value
* @deprecated since 2.4.0 in favor of {@link #of(String, String)}
*/
@Deprecated
public Pair(String name, String value) {
Assert.hasLength(name, "Name must not be empty");
this.name = name;
@ -276,7 +346,24 @@ public final class TestPropertyValues {
return Math.min(colonIndex, equalIndex);
}
private static Pair of(String name, String value) {
/**
* Factory method to create a {@link Pair} from a {@code Map.Entry}.
* @param entry the map entry
* @return the {@link Pair} instance or {@code null}
* @since 2.4.0
*/
public static Pair fromMapEntry(Map.Entry<String, String> entry) {
return (entry != null) ? of(entry.getKey(), entry.getValue()) : null;
}
/**
* Factory method to create a {@link Pair} from a name and value.
* @param name the name
* @param value the value
* @return the {@link Pair} instance or {@code null}
* @since 2.4.0
*/
public static Pair of(String name, String value) {
if (StringUtils.hasLength(name) || StringUtils.hasLength(value)) {
return new Pair(name, value);
}

View File

@ -16,8 +16,14 @@
package org.springframework.boot.test.util;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.util.TestPropertyValues.Pair;
import org.springframework.boot.test.util.TestPropertyValues.Type;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
@ -25,6 +31,7 @@ import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.SystemEnvironmentPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
/**
* Tests for {@link TestPropertyValues}.
@ -36,6 +43,47 @@ class TestPropertyValuesTests {
private final ConfigurableEnvironment environment = new StandardEnvironment();
@Test
void ofStringArrayCreatesValues() {
TestPropertyValues.of("spring:boot", "version:latest").applyTo(this.environment);
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
assertThat(this.environment.getProperty("version")).isEqualTo("latest");
}
@Test
void ofIterableCreatesValues() {
TestPropertyValues.of(Arrays.asList("spring:boot", "version:latest")).applyTo(this.environment);
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
assertThat(this.environment.getProperty("version")).isEqualTo("latest");
}
@Test
void ofStreamCreatesValues() {
TestPropertyValues.of(Stream.of("spring:boot", "version:latest")).applyTo(this.environment);
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
assertThat(this.environment.getProperty("version")).isEqualTo("latest");
}
@Test
void ofMapCreatesValues() {
Map<String, String> map = new LinkedHashMap<>();
map.put("spring", "boot");
map.put("version", "latest");
TestPropertyValues.of(map).applyTo(this.environment);
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
assertThat(this.environment.getProperty("version")).isEqualTo("latest");
}
@Test
void ofMappedStreamCreatesValues() {
TestPropertyValues.of(Stream.of("spring|boot", "version|latest"), (string) -> {
String[] split = string.split("\\|");
return Pair.of(split[0], split[1]);
}).applyTo(this.environment);
assertThat(this.environment.getProperty("spring")).isEqualTo("boot");
assertThat(this.environment.getProperty("version")).isEqualTo("latest");
}
@Test
void applyToEnvironmentShouldAttachConfigurationPropertySource() {
TestPropertyValues.of("foo.bar=baz").applyTo(this.environment);
@ -133,4 +181,23 @@ class TestPropertyValuesTests {
}
}
@Test
void pairOfCreatesPair() {
Map<String, Object> map = new LinkedHashMap<>();
Pair.of("spring", "boot").addTo(map);
assertThat(map).containsOnly(entry("spring", "boot"));
}
@Test
void pairOfWhenNameAndValueAreEmptyReturnsNull() {
assertThat(Pair.of("", "")).isNull();
}
@Test
void pairFromMapEntryCreatesPair() {
Map<String, Object> map = new LinkedHashMap<>();
Pair.fromMapEntry(entry("spring", "boot")).addTo(map);
assertThat(map).containsOnly(entry("spring", "boot"));
}
}