Commit 46099c75 authored by Phillip Webb's avatar Phillip Webb

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
parent e5f22411
/*
* 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);
}
}
/*
* 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">
* &#064;Retention(RUNTIME)
* &#064;PropertyMapping("my.example")
* public &#064;interface Example {
*
* String name();
*
* }
* </pre> When used on a test class as follows: <pre class="code">
* &#064;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;
}
/*
* 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;
}
}
}
/*
* 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);
}
}
/*
* 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;
# 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=\
......
/*
* 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";
}
}
/*
* 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();
}
/*
* 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";
}
}
/*
* 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");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment