Add @PropertyMapping support
Add @PropertyMapping annotation which can be used to mark annotation attributes that should contribute Environment properties. Provides a quick way for tests to change auto-configuration behavior in a structured way. Fixes gh-4901
This commit is contained in:
parent
e5f224118b
commit
46099c753b
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnumerablePropertySource} to adapt annotations marked with
|
||||
* {@link PropertyMapping @PropertyMapping}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>> {
|
||||
|
||||
private static final Pattern CAMEL_CASE_PATTERN = Pattern.compile("([^A-Z-])([A-Z])");
|
||||
|
||||
private final Map<String, Object> properties;
|
||||
|
||||
public AnnotationsPropertySource(Class<?> source) {
|
||||
this("Annotations", source);
|
||||
}
|
||||
|
||||
public AnnotationsPropertySource(String name, Class<?> source) {
|
||||
super(name, source);
|
||||
this.properties = getProperties(source);
|
||||
}
|
||||
|
||||
private Map<String, Object> getProperties(Class<?> source) {
|
||||
Map<String, Object> properties = new LinkedHashMap<String, Object>();
|
||||
collectProperties(source, properties);
|
||||
return Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
||||
private void collectProperties(Class<?> source, Map<String, Object> properties) {
|
||||
if (source != null) {
|
||||
for (Annotation annotation : source.getDeclaredAnnotations()) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||
PropertyMapping typeMapping = AnnotationUtils.getAnnotation(
|
||||
annotation.annotationType(), PropertyMapping.class);
|
||||
for (Method attribute : annotation.annotationType()
|
||||
.getDeclaredMethods()) {
|
||||
collectProperties(annotation, attribute, typeMapping, properties);
|
||||
}
|
||||
}
|
||||
}
|
||||
collectProperties(source.getSuperclass(), properties);
|
||||
}
|
||||
}
|
||||
|
||||
private void collectProperties(Annotation annotation, Method attribute,
|
||||
PropertyMapping typeMapping, Map<String, Object> properties) {
|
||||
PropertyMapping attributeMapping = AnnotationUtils.getAnnotation(attribute,
|
||||
PropertyMapping.class);
|
||||
if (isMapped(typeMapping, attributeMapping)) {
|
||||
String name = getName(typeMapping, attributeMapping, attribute);
|
||||
ReflectionUtils.makeAccessible(attribute);
|
||||
Object value = ReflectionUtils.invokeMethod(attribute, annotation);
|
||||
putProperties(name, value, properties);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMapped(PropertyMapping typeMapping,
|
||||
PropertyMapping attributeMapping) {
|
||||
if (attributeMapping != null) {
|
||||
return attributeMapping.map();
|
||||
}
|
||||
return (typeMapping != null && typeMapping.map());
|
||||
}
|
||||
|
||||
private String getName(PropertyMapping typeMapping, PropertyMapping attributeMapping,
|
||||
Method attribute) {
|
||||
String prefix = (typeMapping == null ? "" : typeMapping.value());
|
||||
String name = (attributeMapping == null ? "" : attributeMapping.value());
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = toKebabCase(attribute.getName());
|
||||
}
|
||||
return dotAppend(prefix, name);
|
||||
}
|
||||
|
||||
private String toKebabCase(String name) {
|
||||
Matcher matcher = CAMEL_CASE_PATTERN.matcher(name);
|
||||
StringBuffer result = new StringBuffer();
|
||||
while (matcher.find()) {
|
||||
matcher.appendReplacement(result,
|
||||
matcher.group(1) + '-' + StringUtils.uncapitalize(matcher.group(2)));
|
||||
}
|
||||
matcher.appendTail(result);
|
||||
return result.toString().toLowerCase();
|
||||
}
|
||||
|
||||
private String dotAppend(String prefix, String postfix) {
|
||||
prefix = (prefix == null ? "" : prefix);
|
||||
if (StringUtils.hasText(prefix)) {
|
||||
return (prefix.endsWith(".") ? prefix + postfix : prefix + "." + postfix);
|
||||
}
|
||||
return postfix;
|
||||
}
|
||||
|
||||
private void putProperties(String name, Object value,
|
||||
Map<String, Object> properties) {
|
||||
if (ObjectUtils.isArray(value)) {
|
||||
Object[] array = ObjectUtils.toObjectArray(value);
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
properties.put(name + "[" + i + "]", array[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
properties.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsProperty(String name) {
|
||||
return this.properties.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getProperty(String name) {
|
||||
return this.properties.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getPropertyNames() {
|
||||
return StringUtils.toStringArray(this.properties.keySet());
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.properties.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.properties.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.properties.equals(((AnnotationsPropertySource) obj).properties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* Indicates that attributes from a test annotation should be mapped into a
|
||||
* {@link PropertySource}. Can be used at the type level, or on individual attributes. For
|
||||
* example, the following annotation declaration: <pre class="code">
|
||||
* @Retention(RUNTIME)
|
||||
* @PropertyMapping("my.example")
|
||||
* public @interface Example {
|
||||
*
|
||||
* String name();
|
||||
*
|
||||
* }
|
||||
* </pre> When used on a test class as follows: <pre class="code">
|
||||
* @Example(name="Spring")
|
||||
* public class MyTest {
|
||||
* }
|
||||
* </pre> will result in a {@literal my.example.name} property being added with the value
|
||||
* {@literal "Spring"}.
|
||||
* <p>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
* @see AnnotationsPropertySource
|
||||
* @see TestPropertySource
|
||||
*/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
public @interface PropertyMapping {
|
||||
|
||||
/**
|
||||
* Defines the property mapping. When used at the type-level, this value will be used
|
||||
* as a prefix for all mapped attributes. When used on an attribute, the value
|
||||
* overrides the generated (kebab case) name.
|
||||
*/
|
||||
String value() default "";
|
||||
|
||||
/**
|
||||
* Determines if mapping should occur. When specified at the type-level indicates if
|
||||
* mapping should occur by default or not. When used at the attribute-level, overrides
|
||||
* the type-level default.
|
||||
*/
|
||||
boolean map() default true;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.MergedContextConfiguration;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizer} to map annotation attributes to {@link Environment}
|
||||
* properties.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertyMappingContextCustomizer implements ContextCustomizer {
|
||||
|
||||
private final AnnotationsPropertySource propertySource;
|
||||
|
||||
PropertyMappingContextCustomizer(AnnotationsPropertySource propertySource) {
|
||||
this.propertySource = propertySource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customizeContext(ConfigurableApplicationContext context,
|
||||
MergedContextConfiguration mergedContextConfiguration) {
|
||||
if (!this.propertySource.isEmpty()) {
|
||||
context.getEnvironment().getPropertySources().addFirst(this.propertySource);
|
||||
}
|
||||
context.getBeanFactory().registerSingleton(
|
||||
PropertyMappingCheckBeanPostProcessor.class.getName(),
|
||||
new PropertyMappingCheckBeanPostProcessor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.propertySource.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj != null && getClass().equals(obj.getClass()) && this.propertySource
|
||||
.equals(((PropertyMappingContextCustomizer) obj).propertySource));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanPostProcessor} to check that {@link PropertyMapping} is only used on
|
||||
* test classes.
|
||||
*/
|
||||
static class PropertyMappingCheckBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
Class<?> beanClass = bean.getClass();
|
||||
boolean hasComponent = AnnotationUtils.findAnnotation(beanClass,
|
||||
Component.class) != null;
|
||||
boolean hasPropertyMapping = AnnotationUtils.findAnnotation(beanClass,
|
||||
PropertyMapping.class) != null;
|
||||
if (hasComponent) {
|
||||
Assert.state(!hasPropertyMapping,
|
||||
"@PropertyMapping annotations can only be used on test classes");
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.ContextConfigurationAttributes;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
import org.springframework.test.context.ContextCustomizerFactory;
|
||||
|
||||
/**
|
||||
* {@link ContextCustomizerFactory} to map annotation attributes to {@link Environment}
|
||||
* properties.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class PropertyMappingContextCustomizerFactory implements ContextCustomizerFactory {
|
||||
|
||||
@Override
|
||||
public ContextCustomizer createContextCustomizer(Class<?> testClass,
|
||||
List<ContextConfigurationAttributes> configurationAttributes) {
|
||||
AnnotationsPropertySource propertySource = new AnnotationsPropertySource(
|
||||
testClass);
|
||||
return new PropertyMappingContextCustomizer(propertySource);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for mapping annotation attribute values in the Spring {@code Environment}.
|
||||
*/
|
||||
package org.springframework.boot.test.autoconfigure.properties;
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
# Spring Test ContextCustomizerFactories
|
||||
org.springframework.test.context.ContextCustomizerFactory=\
|
||||
org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory
|
||||
org.springframework.boot.test.autoconfigure.OverrideAutoConfigurationContextCustomizerFactory,\
|
||||
org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizerFactory
|
||||
|
||||
# Test Execution Listeners
|
||||
org.springframework.test.context.TestExecutionListener=\
|
||||
|
|
|
|||
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AnnotationsPropertySource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AnnotationsPropertySourceTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Test
|
||||
public void createWhenSourceIsNullShouldThrowException() throws Exception {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("Property source must not be null");
|
||||
new AnnotationsPropertySource(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenHasNoAnnotationShouldBeEmpty() throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
NoAnnotation.class);
|
||||
assertThat(source.getPropertyNames()).isEmpty();
|
||||
assertThat(source.getProperty("value")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenHasTypeLevelAnnotationShouldUseAttributeName()
|
||||
throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(TypeLevel.class);
|
||||
assertThat(source.getPropertyNames()).containsExactly("value");
|
||||
assertThat(source.getProperty("value")).isEqualTo("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenHasTypeLevelWithPrefixShouldUsePrefixedName()
|
||||
throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
TypeLevelWithPrefix.class);
|
||||
assertThat(source.getPropertyNames()).containsExactly("test.value");
|
||||
assertThat(source.getProperty("test.value")).isEqualTo("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenHasAttributeLevelWithPrefixShouldUsePrefixedName()
|
||||
throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
AttributeLevelWithPrefix.class);
|
||||
assertThat(source.getPropertyNames()).containsExactly("test");
|
||||
assertThat(source.getProperty("test")).isEqualTo("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenHasTypeAndAttributeLevelWithPrefixShouldUsePrefixedName()
|
||||
throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
TypeAndAttributeLevelWithPrefix.class);
|
||||
assertThat(source.getPropertyNames()).containsExactly("test.example");
|
||||
assertThat(source.getProperty("test.example")).isEqualTo("abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenNotMappedAtTypeLevelShouldIgnoreAttributes()
|
||||
throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
NotMappedAtTypeLevel.class);
|
||||
assertThat(source.getPropertyNames()).containsExactly("value");
|
||||
assertThat(source.getProperty("ignore")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenNotMappedAtAttributeLevelShouldIgnoreAttributes()
|
||||
throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
NotMappedAtAttributeLevel.class);
|
||||
assertThat(source.getPropertyNames()).containsExactly("value");
|
||||
assertThat(source.getProperty("ignore")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenCountainsArryasShouldExpandNames() throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(Arrays.class);
|
||||
assertThat(source.getPropertyNames()).contains("strings[0]", "strings[1]",
|
||||
"classes[0]", "classes[1]", "ints[0]", "ints[1]", "longs[0]", "longs[1]",
|
||||
"floats[0]", "floats[1]", "doubles[0]", "doubles[1]", "booleans[0]",
|
||||
"booleans[1]");
|
||||
assertThat(source.getProperty("strings[0]")).isEqualTo("a");
|
||||
assertThat(source.getProperty("strings[1]")).isEqualTo("b");
|
||||
assertThat(source.getProperty("classes[0]")).isEqualTo(Integer.class);
|
||||
assertThat(source.getProperty("classes[1]")).isEqualTo(Long.class);
|
||||
assertThat(source.getProperty("ints[0]")).isEqualTo(1);
|
||||
assertThat(source.getProperty("ints[1]")).isEqualTo(2);
|
||||
assertThat(source.getProperty("longs[0]")).isEqualTo(1L);
|
||||
assertThat(source.getProperty("longs[1]")).isEqualTo(2L);
|
||||
assertThat(source.getProperty("floats[0]")).isEqualTo(1.0f);
|
||||
assertThat(source.getProperty("floats[1]")).isEqualTo(2.0f);
|
||||
assertThat(source.getProperty("doubles[0]")).isEqualTo(1.0);
|
||||
assertThat(source.getProperty("doubles[1]")).isEqualTo(2.0);
|
||||
assertThat(source.getProperty("booleans[0]")).isEqualTo(false);
|
||||
assertThat(source.getProperty("booleans[1]")).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesWhenHasCamelCaseShouldConvertToKebabCase() throws Exception {
|
||||
AnnotationsPropertySource source = new AnnotationsPropertySource(
|
||||
CamelCaseToKebabCase.class);
|
||||
assertThat(source.getPropertyNames()).contains("camel-case-to-kebab-case");
|
||||
}
|
||||
|
||||
static class NoAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@TypeLevelAnnotation("abc")
|
||||
static class TypeLevel {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping
|
||||
static @interface TypeLevelAnnotation {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
@TypeLevelWithPrefixAnnotation("abc")
|
||||
static class TypeLevelWithPrefix {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping("test")
|
||||
static @interface TypeLevelWithPrefixAnnotation {
|
||||
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
@AttributeLevelWithPrefixAnnotation("abc")
|
||||
static class AttributeLevelWithPrefix {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface AttributeLevelWithPrefixAnnotation {
|
||||
|
||||
@PropertyMapping("test")
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
@TypeAndAttributeLevelWithPrefixAnnotation("abc")
|
||||
static class TypeAndAttributeLevelWithPrefix {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping("test")
|
||||
static @interface TypeAndAttributeLevelWithPrefixAnnotation {
|
||||
|
||||
@PropertyMapping("example")
|
||||
String value();
|
||||
|
||||
}
|
||||
|
||||
@NotMappedAtTypeLevelAnnotation("abc")
|
||||
static class NotMappedAtTypeLevel {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping(map = false)
|
||||
static @interface NotMappedAtTypeLevelAnnotation {
|
||||
|
||||
@PropertyMapping
|
||||
String value();
|
||||
|
||||
String ignore() default "xyz";
|
||||
|
||||
}
|
||||
|
||||
@NotMappedAtAttributeLevelAnnotation("abc")
|
||||
static class NotMappedAtAttributeLevel {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping
|
||||
static @interface NotMappedAtAttributeLevelAnnotation {
|
||||
|
||||
String value();
|
||||
|
||||
@PropertyMapping(map = false)
|
||||
String ignore() default "xyz";
|
||||
|
||||
}
|
||||
|
||||
@ArraysAnnotation(strings = { "a", "b" }, classes = { Integer.class,
|
||||
Long.class }, ints = { 1, 2 }, longs = { 1, 2 }, floats = { 1.0f,
|
||||
2.0f }, doubles = { 1.0, 2.0 }, booleans = { false, true })
|
||||
static class Arrays {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping
|
||||
static @interface ArraysAnnotation {
|
||||
|
||||
String[] strings();
|
||||
|
||||
Class<?>[] classes();
|
||||
|
||||
int[] ints();
|
||||
|
||||
long[] longs();
|
||||
|
||||
float[] floats();
|
||||
|
||||
double[] doubles();
|
||||
|
||||
boolean[] booleans();
|
||||
|
||||
}
|
||||
|
||||
@CamelCaseToKebabCaseAnnotation
|
||||
static class CamelCaseToKebabCase {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping
|
||||
static @interface CamelCaseToKebabCaseAnnotation {
|
||||
|
||||
String camelCaseToKebabCase() default "abc";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Example {@link PropertyMapping} annotation for use wuth {@link PropertyMappingTests}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping
|
||||
@interface ExampleMapping {
|
||||
|
||||
String exampleProperty();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.test.context.ContextCustomizer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertyMappingContextCustomizerFactory}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class PropertyMappingContextCustomizerFactoryTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private PropertyMappingContextCustomizerFactory factory = new PropertyMappingContextCustomizerFactory();
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenHasNoMappingShouldNotAddPropertySource() {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(NoMapping.class, null);
|
||||
ConfigurableApplicationContext context = mock(
|
||||
ConfigurableApplicationContext.class);
|
||||
ConfigurableEnvironment environment = mock(ConfigurableEnvironment.class);
|
||||
ConfigurableListableBeanFactory beanFactory = mock(
|
||||
ConfigurableListableBeanFactory.class);
|
||||
given(context.getEnvironment()).willReturn(environment);
|
||||
given(context.getBeanFactory()).willReturn(beanFactory);
|
||||
customizer.customizeContext(context, null);
|
||||
verifyZeroInteractions(environment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenHasTypeMappingShouldReturnCustomizer() {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(TypeMapping.class, null);
|
||||
assertThat(customizer).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getContextCustomizerWhenHasAttributeMappingShouldReturnCustomizer() {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(AttributeMapping.class, null);
|
||||
assertThat(customizer).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hashCodeAndEqualsShoudBeBasedOnPropertyValues() throws Exception {
|
||||
ContextCustomizer customizer1 = this.factory
|
||||
.createContextCustomizer(TypeMapping.class, null);
|
||||
ContextCustomizer customizer2 = this.factory
|
||||
.createContextCustomizer(AttributeMapping.class, null);
|
||||
ContextCustomizer customizer3 = this.factory
|
||||
.createContextCustomizer(OtherMapping.class, null);
|
||||
assertThat(customizer1.hashCode()).isEqualTo(customizer2.hashCode());
|
||||
assertThat(customizer1).isEqualTo(customizer1).isEqualTo(customizer2)
|
||||
.isNotEqualTo(customizer3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prepareContextShouldAddPropertySource() throws Exception {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(AttributeMapping.class, null);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
customizer.customizeContext(context, null);
|
||||
assertThat(context.getEnvironment().getProperty("mapped")).isEqualTo("Mapped");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertyMappingShouldNotBeUsedWithComponent() throws Exception {
|
||||
ContextCustomizer customizer = this.factory
|
||||
.createContextCustomizer(AttributeMapping.class, null);
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(ConfigMapping.class);
|
||||
customizer.customizeContext(context, null);
|
||||
this.thrown.expect(BeanCreationException.class);
|
||||
this.thrown.expectMessage(
|
||||
"@PropertyMapping annotations can only be used on test classes");
|
||||
context.refresh();
|
||||
}
|
||||
|
||||
@NoMappingAnnotation
|
||||
static class NoMapping {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface NoMappingAnnotation {
|
||||
|
||||
}
|
||||
|
||||
@TypeMappingAnnotation
|
||||
static class TypeMapping {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@TypeMappingAnnotation
|
||||
static class ConfigMapping {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@PropertyMapping
|
||||
static @interface TypeMappingAnnotation {
|
||||
|
||||
String mapped() default "Mapped";
|
||||
|
||||
}
|
||||
|
||||
@AttributeMappingAnnotation
|
||||
static class AttributeMapping {
|
||||
}
|
||||
|
||||
@AttributeMappingAnnotation("Other")
|
||||
static class OtherMapping {
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
static @interface AttributeMappingAnnotation {
|
||||
|
||||
@PropertyMapping("mapped")
|
||||
String value() default "Mapped";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright 2012-2016 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.autoconfigure.properties;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link PropertyMapping @PropertyMapping} annotaions.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ExampleMapping(exampleProperty = "abc")
|
||||
public class PropertyMappingTests {
|
||||
|
||||
@Autowired
|
||||
private Environment environment;
|
||||
|
||||
@Test
|
||||
public void hasProperty() throws Exception {
|
||||
assertThat(this.environment.getProperty("example-property")).isEqualTo("abc");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue