Browse Source
This commit introduces a @DynamicPropertySource annotation that can be used on methods in test classes that want to add properties to the Environment with a dynamically supplied value. This new feature can be used in conjunction with Testcontainers and other frameworks that manage resources outside the lifecycle of a test's ApplicationContext. Closes gh-24540 Co-authored-by: Phillip Webb <pwebb@pivotal.io>pull/24766/head
Sam Brannen
5 years ago
11 changed files with 781 additions and 2 deletions
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
/** |
||||
* Registry used with {@link DynamicPropertySource @DynamicPropertySource} |
||||
* methods so that they can add properties to the {@code Environment} that have |
||||
* dynamically resolved values. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
* @since 5.2.5 |
||||
* @see DynamicPropertySource |
||||
*/ |
||||
public interface DynamicPropertyRegistry { |
||||
|
||||
/** |
||||
* Add a {@link Supplier} for the given property name to this registry. |
||||
* @param name the name of the property for which the supplier should be added |
||||
* @param valueSupplier a supplier that will provide the property value on demand |
||||
*/ |
||||
void add(String name, Supplier<Object> valueSupplier); |
||||
|
||||
} |
@ -0,0 +1,75 @@
@@ -0,0 +1,75 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context; |
||||
|
||||
import java.lang.annotation.Documented; |
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* Method-level annotation for integration tests that need to add properties with |
||||
* dynamic values to the {@code Environment}'s set of {@code PropertySources}. |
||||
* |
||||
* <p>This annotation and its supporting infrastructure were originally designed |
||||
* to allow properties from |
||||
* <a href="https://www.testcontainers.org/">Testcontainers</a> based tests to be |
||||
* exposed easily to Spring integration tests. However, this feature may also be |
||||
* used with any form of external resource whose lifecycle is maintained outside |
||||
* the test's {@code ApplicationContext}. |
||||
* |
||||
* <p>Methods annotated with {@code @DynamicPropertySource} must be {@code static} |
||||
* and must have a single {@link DynamicPropertyRegistry} argument which is used |
||||
* to add <em>name-value</em> pairs to the {@code Environment}'s set of |
||||
* {@code PropertySources}. Values are dynamic and provided via a {@link Supplier} |
||||
* which is only invoked when the property is resolved. Typically, method references |
||||
* are used to supply values, as in the following example. |
||||
* |
||||
* <h3>Example</h3> |
||||
* |
||||
* <pre class="code"> |
||||
* @SpringJUnitConfig(...) |
||||
* @Testcontainers |
||||
* class ExampleIntegrationTests { |
||||
* |
||||
* @Container |
||||
* static RedisContainer redis = new RedisContainer(); |
||||
* |
||||
* // ...
|
||||
* |
||||
* @DynamicPropertySource |
||||
* static void redisProperties(DynamicPropertyRegistry registry) { |
||||
* registry.add("redis.host", redis::getContainerIpAddress); |
||||
* registry.add("redis.port", redis::getMappedPort); |
||||
* } |
||||
* |
||||
* }</pre> |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
* @since 5.2.5 |
||||
* @see DynamicPropertyRegistry |
||||
* @see ContextConfiguration |
||||
* @see TestPropertySource |
||||
* @see org.springframework.core.env.PropertySource |
||||
*/ |
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
@Documented |
||||
public @interface DynamicPropertySource { |
||||
} |
@ -0,0 +1,111 @@
@@ -0,0 +1,111 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context.support; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.lang.reflect.Modifier; |
||||
import java.util.Collections; |
||||
import java.util.LinkedHashMap; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.core.env.MutablePropertySources; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.test.context.ContextCustomizer; |
||||
import org.springframework.test.context.DynamicPropertyRegistry; |
||||
import org.springframework.test.context.DynamicPropertySource; |
||||
import org.springframework.test.context.MergedContextConfiguration; |
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
/** |
||||
* {@link ContextCustomizer} to support |
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
* @since 5.2.5 |
||||
* @see DynamicPropertiesContextCustomizerFactory |
||||
*/ |
||||
class DynamicPropertiesContextCustomizer implements ContextCustomizer { |
||||
|
||||
private static final String PROPERTY_SOURCE_NAME = "Dynamic Test Properties"; |
||||
|
||||
|
||||
private final Set<Method> methods; |
||||
|
||||
|
||||
DynamicPropertiesContextCustomizer(Set<Method> methods) { |
||||
methods.forEach(this::assertValid); |
||||
this.methods = methods; |
||||
} |
||||
|
||||
|
||||
private void assertValid(Method method) { |
||||
Assert.state(Modifier.isStatic(method.getModifiers()), |
||||
() -> "@DynamicPropertySource method '" + method.getName() + "' must be static"); |
||||
Class<?>[] types = method.getParameterTypes(); |
||||
Assert.state(types.length == 1 && types[0] == DynamicPropertyRegistry.class, |
||||
() -> "@DynamicPropertySource method '" + method.getName() + "' must accept a single DynamicPropertyRegistry argument"); |
||||
} |
||||
|
||||
@Override |
||||
public void customizeContext(ConfigurableApplicationContext context, |
||||
MergedContextConfiguration mergedConfig) { |
||||
|
||||
MutablePropertySources sources = context.getEnvironment().getPropertySources(); |
||||
sources.addFirst(new DynamicValuesPropertySource(PROPERTY_SOURCE_NAME, buildDynamicPropertiesMap())); |
||||
} |
||||
|
||||
@Nullable |
||||
private Map<String, Supplier<Object>> buildDynamicPropertiesMap() { |
||||
Map<String, Supplier<Object>> map = new LinkedHashMap<>(); |
||||
DynamicPropertyRegistry dynamicPropertyRegistry = (name, valueSupplier) -> { |
||||
Assert.hasText(name, "'name' must not be null or blank"); |
||||
Assert.notNull(valueSupplier, "'valueSupplier' must not be null"); |
||||
map.put(name, valueSupplier); |
||||
}; |
||||
this.methods.forEach(method -> { |
||||
ReflectionUtils.makeAccessible(method); |
||||
ReflectionUtils.invokeMethod(method, null, dynamicPropertyRegistry); |
||||
}); |
||||
return Collections.unmodifiableMap(map); |
||||
} |
||||
|
||||
Set<Method> getMethods() { |
||||
return this.methods; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return this.methods.hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) { |
||||
return true; |
||||
} |
||||
if (obj == null || getClass() != obj.getClass()) { |
||||
return false; |
||||
} |
||||
return this.methods.equals(((DynamicPropertiesContextCustomizer) obj).methods); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context.support; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
|
||||
import org.springframework.core.MethodIntrospector; |
||||
import org.springframework.core.annotation.MergedAnnotations; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.test.context.ContextConfigurationAttributes; |
||||
import org.springframework.test.context.ContextCustomizerFactory; |
||||
import org.springframework.test.context.DynamicPropertySource; |
||||
|
||||
/** |
||||
* {@link ContextCustomizerFactory} to support |
||||
* {@link DynamicPropertySource @DynamicPropertySource} methods. |
||||
* |
||||
* @author Phillip Webb |
||||
* @since 5.2.5 |
||||
* @see DynamicPropertiesContextCustomizer |
||||
*/ |
||||
class DynamicPropertiesContextCustomizerFactory implements ContextCustomizerFactory { |
||||
|
||||
@Override |
||||
@Nullable |
||||
public DynamicPropertiesContextCustomizer createContextCustomizer(Class<?> testClass, |
||||
List<ContextConfigurationAttributes> configAttributes) { |
||||
|
||||
Set<Method> methods = MethodIntrospector.selectMethods(testClass, this::isAnnotated); |
||||
if (methods.isEmpty()) { |
||||
return null; |
||||
} |
||||
return new DynamicPropertiesContextCustomizer(methods); |
||||
} |
||||
|
||||
private boolean isAnnotated(Method method) { |
||||
return MergedAnnotations.from(method).isPresent(DynamicPropertySource.class); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,56 @@
@@ -0,0 +1,56 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context.support; |
||||
|
||||
import java.util.Map; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.core.env.EnumerablePropertySource; |
||||
import org.springframework.util.StringUtils; |
||||
|
||||
/** |
||||
* {@link EnumerablePropertySource} backed by a map with dynamically supplied |
||||
* values. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
* @since 5.2.5 |
||||
*/ |
||||
class DynamicValuesPropertySource extends EnumerablePropertySource<Map<String, Supplier<Object>>> { |
||||
|
||||
DynamicValuesPropertySource(String name, Map<String, Supplier<Object>> valueSuppliers) { |
||||
super(name, valueSuppliers); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public Object getProperty(String name) { |
||||
Supplier<Object> valueSupplier = this.source.get(name); |
||||
return (valueSupplier != null ? valueSupplier.get() : null); |
||||
} |
||||
|
||||
@Override |
||||
public boolean containsProperty(String name) { |
||||
return this.source.containsKey(name); |
||||
} |
||||
|
||||
@Override |
||||
public String[] getPropertyNames() { |
||||
return StringUtils.toStringArray(this.source.keySet()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,96 @@
@@ -0,0 +1,96 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.beans.factory.annotation.Value; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Integration test for {@link DynamicPropertySource @DynamicPropertySource}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
*/ |
||||
@SpringJUnitConfig |
||||
class DynamicPropertySourceIntegrationTests { |
||||
|
||||
static DemoContainer container = new DemoContainer(); |
||||
|
||||
|
||||
@DynamicPropertySource |
||||
static void containerProperties(DynamicPropertyRegistry registry) { |
||||
registry.add("test.container.ip", container::getIpAddress); |
||||
registry.add("test.container.port", container::getPort); |
||||
} |
||||
|
||||
|
||||
@Test |
||||
void hasInjectedValues(@Autowired Service service) { |
||||
assertThat(service.getIp()).isEqualTo("127.0.0.1"); |
||||
assertThat(service.getPort()).isEqualTo(4242); |
||||
} |
||||
|
||||
|
||||
@Configuration |
||||
@Import(Service.class) |
||||
static class Config { |
||||
} |
||||
|
||||
@Component |
||||
static class Service { |
||||
|
||||
private final String ip; |
||||
|
||||
private final int port; |
||||
|
||||
|
||||
Service(@Value("${test.container.ip}") String ip, @Value("${test.container.port}") int port) { |
||||
this.ip = ip; |
||||
this.port = port; |
||||
} |
||||
|
||||
String getIp() { |
||||
return this.ip; |
||||
} |
||||
|
||||
int getPort() { |
||||
return this.port; |
||||
} |
||||
|
||||
} |
||||
|
||||
static class DemoContainer { |
||||
|
||||
String getIpAddress() { |
||||
return "127.0.0.1"; |
||||
} |
||||
|
||||
int getPort() { |
||||
return 4242; |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,121 @@
@@ -0,0 +1,121 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context.support; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.test.context.ContextConfigurationAttributes; |
||||
import org.springframework.test.context.DynamicPropertyRegistry; |
||||
import org.springframework.test.context.DynamicPropertySource; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests for {@link DynamicPropertiesContextCustomizerFactory}. |
||||
* |
||||
* @author Phillip Webb |
||||
*/ |
||||
class DynamicPropertiesContextCustomizerFactoryTests { |
||||
|
||||
private final DynamicPropertiesContextCustomizerFactory factory = new DynamicPropertiesContextCustomizerFactory(); |
||||
|
||||
private final List<ContextConfigurationAttributes> configAttributes = Collections.emptyList(); |
||||
|
||||
@Test |
||||
void createContextCustomizerWhenNoAnnotatedMethodsReturnsNull() { |
||||
DynamicPropertiesContextCustomizer customizer = this.factory.createContextCustomizer( |
||||
NoDynamicPropertySource.class, this.configAttributes); |
||||
assertThat(customizer).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void createContextCustomizerWhenSingleAnnotatedMethodReturnsCustomizer() { |
||||
DynamicPropertiesContextCustomizer customizer = this.factory.createContextCustomizer( |
||||
SingleDynamicPropertySource.class, this.configAttributes); |
||||
assertThat(customizer).isNotNull(); |
||||
assertThat(customizer.getMethods()).flatExtracting(Method::getName).containsOnly("p1"); |
||||
} |
||||
|
||||
@Test |
||||
void createContextCustomizerWhenMultipleAnnotatedMethodsReturnsCustomizer() { |
||||
DynamicPropertiesContextCustomizer customizer = this.factory.createContextCustomizer( |
||||
MultipleDynamicPropertySources.class, this.configAttributes); |
||||
assertThat(customizer).isNotNull(); |
||||
assertThat(customizer.getMethods()).flatExtracting(Method::getName).containsOnly("p1", "p2", "p3"); |
||||
} |
||||
|
||||
@Test |
||||
void createContextCustomizerWhenAnnotatedMethodsInBaseClassReturnsCustomizer() { |
||||
DynamicPropertiesContextCustomizer customizer = this.factory.createContextCustomizer( |
||||
SubDynamicPropertySource.class, this.configAttributes); |
||||
assertThat(customizer).isNotNull(); |
||||
assertThat(customizer.getMethods()).flatExtracting(Method::getName).containsOnly("p1", "p2"); |
||||
} |
||||
|
||||
|
||||
static class NoDynamicPropertySource { |
||||
|
||||
void empty() { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class SingleDynamicPropertySource { |
||||
|
||||
@DynamicPropertySource |
||||
static void p1(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class MultipleDynamicPropertySources { |
||||
|
||||
@DynamicPropertySource |
||||
static void p1(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
@DynamicPropertySource |
||||
static void p2(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
@DynamicPropertySource |
||||
static void p3(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class BaseDynamicPropertySource { |
||||
|
||||
@DynamicPropertySource |
||||
static void p1(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
} |
||||
|
||||
static class SubDynamicPropertySource extends BaseDynamicPropertySource { |
||||
|
||||
@DynamicPropertySource |
||||
static void p2(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,153 @@
@@ -0,0 +1,153 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context.support; |
||||
|
||||
import java.lang.reflect.Method; |
||||
import java.util.Arrays; |
||||
import java.util.LinkedHashSet; |
||||
import java.util.Set; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.context.ConfigurableApplicationContext; |
||||
import org.springframework.context.support.StaticApplicationContext; |
||||
import org.springframework.core.env.ConfigurableEnvironment; |
||||
import org.springframework.test.context.DynamicPropertyRegistry; |
||||
import org.springframework.test.context.MergedContextConfiguration; |
||||
import org.springframework.util.ObjectUtils; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; |
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException; |
||||
import static org.mockito.Mockito.mock; |
||||
|
||||
/** |
||||
* Tests for {@link DynamicPropertiesContextCustomizer}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
*/ |
||||
class DynamicPropertiesContextCustomizerTests { |
||||
|
||||
@Test |
||||
void createWhenNonStaticDynamicPropertiesMethodThrowsException() { |
||||
assertThatIllegalStateException() |
||||
.isThrownBy(() -> customizerFor("nonStatic")) |
||||
.withMessage("@DynamicPropertySource method 'nonStatic' must be static"); |
||||
} |
||||
|
||||
@Test |
||||
void createWhenBadDynamicPropertiesSignatureThrowsException() { |
||||
assertThatIllegalStateException() |
||||
.isThrownBy(() -> customizerFor("badArgs")) |
||||
.withMessage("@DynamicPropertySource method 'badArgs' must accept a single DynamicPropertyRegistry argument"); |
||||
} |
||||
|
||||
@Test |
||||
void nullPropertyNameResultsInException() throws Exception { |
||||
DynamicPropertiesContextCustomizer customizer = customizerFor("nullName"); |
||||
ConfigurableApplicationContext context = new StaticApplicationContext(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> customizer.customizeContext(context, mock(MergedContextConfiguration.class))) |
||||
.withMessage("'name' must not be null or blank"); |
||||
} |
||||
|
||||
@Test |
||||
void emptyPropertyNameResultsInException() throws Exception { |
||||
DynamicPropertiesContextCustomizer customizer = customizerFor("emptyName"); |
||||
ConfigurableApplicationContext context = new StaticApplicationContext(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> customizer.customizeContext(context, mock(MergedContextConfiguration.class))) |
||||
.withMessage("'name' must not be null or blank"); |
||||
} |
||||
|
||||
@Test |
||||
void nullValueSupplierResultsInException() throws Exception { |
||||
DynamicPropertiesContextCustomizer customizer = customizerFor("nullValueSupplier"); |
||||
ConfigurableApplicationContext context = new StaticApplicationContext(); |
||||
assertThatIllegalArgumentException() |
||||
.isThrownBy(() -> customizer.customizeContext(context, mock(MergedContextConfiguration.class))) |
||||
.withMessage("'valueSupplier' must not be null"); |
||||
} |
||||
|
||||
@Test |
||||
void customizeContextAddsPropertySource() throws Exception { |
||||
ConfigurableApplicationContext context = new StaticApplicationContext(); |
||||
DynamicPropertiesContextCustomizer customizer = customizerFor("valid1", "valid2"); |
||||
customizer.customizeContext(context, mock(MergedContextConfiguration.class)); |
||||
ConfigurableEnvironment environment = context.getEnvironment(); |
||||
assertThat(environment.getRequiredProperty("p1a")).isEqualTo("v1a"); |
||||
assertThat(environment.getRequiredProperty("p1b")).isEqualTo("v1b"); |
||||
assertThat(environment.getRequiredProperty("p2a")).isEqualTo("v2a"); |
||||
assertThat(environment.getRequiredProperty("p2b")).isEqualTo("v2b"); |
||||
} |
||||
|
||||
@Test |
||||
void equalsAndHashCode() { |
||||
DynamicPropertiesContextCustomizer c1 = customizerFor("valid1", "valid2"); |
||||
DynamicPropertiesContextCustomizer c2 = customizerFor("valid1", "valid2"); |
||||
DynamicPropertiesContextCustomizer c3 = customizerFor("valid1"); |
||||
assertThat(c1.hashCode()).isEqualTo(c1.hashCode()).isEqualTo(c2.hashCode()); |
||||
assertThat(c1).isEqualTo(c1).isEqualTo(c2).isNotEqualTo(c3); |
||||
} |
||||
|
||||
|
||||
private static DynamicPropertiesContextCustomizer customizerFor(String...methods) { |
||||
return new DynamicPropertiesContextCustomizer(findMethods(methods)); |
||||
} |
||||
|
||||
private static Set<Method> findMethods(String... names) { |
||||
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(DynamicPropertySourceTestCase.class, |
||||
method -> ObjectUtils.containsElement(names, method.getName())); |
||||
return new LinkedHashSet<>(Arrays.asList(methods)); |
||||
} |
||||
|
||||
|
||||
static class DynamicPropertySourceTestCase { |
||||
|
||||
void nonStatic(DynamicPropertyRegistry registry) { |
||||
} |
||||
|
||||
static void badArgs(String bad) { |
||||
} |
||||
|
||||
static void nullName(DynamicPropertyRegistry registry) { |
||||
registry.add(null, () -> "A"); |
||||
} |
||||
|
||||
static void emptyName(DynamicPropertyRegistry registry) { |
||||
registry.add(" ", () -> "A"); |
||||
} |
||||
|
||||
static void nullValueSupplier(DynamicPropertyRegistry registry) { |
||||
registry.add("name", null); |
||||
} |
||||
|
||||
static void valid1(DynamicPropertyRegistry registry) { |
||||
registry.add("p1a", () -> "v1a"); |
||||
registry.add("p1b", () -> "v1b"); |
||||
} |
||||
|
||||
static void valid2(DynamicPropertyRegistry registry) { |
||||
registry.add("p2a", () -> "v2a"); |
||||
registry.add("p2b", () -> "v2b"); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,69 @@
@@ -0,0 +1,69 @@
|
||||
/* |
||||
* Copyright 2002-2020 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 |
||||
* |
||||
* https://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.test.context.support; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Unit tests for {@link DynamicValuesPropertySource}. |
||||
* |
||||
* @author Phillip Webb |
||||
* @author Sam Brannen |
||||
*/ |
||||
class DynamicValuesPropertySourceTests { |
||||
|
||||
@SuppressWarnings("serial") |
||||
private final DynamicValuesPropertySource source = new DynamicValuesPropertySource("test", |
||||
new HashMap<String, Supplier<Object>>() {{ |
||||
put("a", () -> "A"); |
||||
put("b", () -> "B"); |
||||
}}); |
||||
|
||||
|
||||
@Test |
||||
void getPropertyReturnsSuppliedProperty() throws Exception { |
||||
assertThat(this.source.getProperty("a")).isEqualTo("A"); |
||||
assertThat(this.source.getProperty("b")).isEqualTo("B"); |
||||
} |
||||
|
||||
@Test |
||||
void getPropertyWhenMissingReturnsNull() throws Exception { |
||||
assertThat(this.source.getProperty("c")).isNull(); |
||||
} |
||||
|
||||
@Test |
||||
void containsPropertyWhenPresentReturnsTrue() { |
||||
assertThat(this.source.containsProperty("a")).isTrue(); |
||||
assertThat(this.source.containsProperty("b")).isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
void containsPropertyWhenMissingReturnsFalse() { |
||||
assertThat(this.source.containsProperty("c")).isFalse(); |
||||
} |
||||
|
||||
@Test |
||||
void getPropertyNamesReturnsNames() { |
||||
assertThat(this.source.getPropertyNames()).containsExactly("a", "b"); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue