Skip to content

Support @TestPropertySource as a repeatable annotation #23320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2019 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.
Expand All @@ -19,6 +19,7 @@
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand Down Expand Up @@ -86,6 +87,7 @@
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Repeatable(TestPropertySources.class)
public @interface TestPropertySource {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2002-2019 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.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* {@code @TestPropertySources} is a container for one or more {@link TestPropertySource}
* declarations.
*
* <p>Note, however, that use of the {@code @TestPropertySources} container is completely
* optional since {@code @TestPropertySource} is a {@linkplain java.lang.annotation.Repeatable
* repeatable} annotation.
*
* @author Anatoliy Korovin
* @since 5.2
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TestPropertySources {

/**
* An array of one or more {@link TestPropertySource} declarations.
*
* @return array of {@link TestPropertySource} values.
*/
TestPropertySource[] value();
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 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.
Expand All @@ -24,11 +24,14 @@
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.MergedAnnotation;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
Expand All @@ -39,20 +42,18 @@
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.util.TestContextResourceUtils;
import org.springframework.test.util.MetaAnnotationUtils.AnnotationDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import static org.springframework.test.util.MetaAnnotationUtils.findAnnotationDescriptor;

/**
* Utility methods for working with {@link TestPropertySource @TestPropertySource}
* and adding test {@link PropertySource PropertySources} to the {@code Environment}.
*
* <p>Primarily intended for use within the framework.
*
* @author Sam Brannen
* @author Anatoliy Korovin
* @since 4.1
* @see TestPropertySource
*/
Expand All @@ -67,47 +68,59 @@ public abstract class TestPropertySourceUtils {

private static final Log logger = LogFactory.getLog(TestPropertySourceUtils.class);


static MergedTestPropertySources buildMergedTestPropertySources(Class<?> testClass) {
Class<TestPropertySource> annotationType = TestPropertySource.class;
AnnotationDescriptor<TestPropertySource> descriptor = findAnnotationDescriptor(testClass, annotationType);
if (descriptor == null) {

if (!isPresentTestPropertySourceAnnotation(testClass)) {
return new MergedTestPropertySources();
}
else {
return mergeTestPropertySources(testClass);
}
}

private static boolean isPresentTestPropertySourceAnnotation(Class<?> testClass) {
return MergedAnnotations
.from(testClass, MergedAnnotations.SearchStrategy.EXHAUSTIVE)
.get(TestPropertySource.class).isPresent();
}

private static MergedTestPropertySources mergeTestPropertySources(Class<?> testClass) {

List<TestPropertySourceAttributes> attributesList = resolveTestPropertySourceAttributes(
testClass);

List<TestPropertySourceAttributes> attributesList = resolveTestPropertySourceAttributes(testClass);
String[] locations = mergeLocations(attributesList);
String[] properties = mergeProperties(attributesList);

return new MergedTestPropertySources(locations, properties);
}

private static List<TestPropertySourceAttributes> resolveTestPropertySourceAttributes(Class<?> testClass) {
Assert.notNull(testClass, "Class must not be null");
List<TestPropertySourceAttributes> attributesList = new ArrayList<>();
Class<TestPropertySource> annotationType = TestPropertySource.class;
return MergedAnnotations
.from(testClass, MergedAnnotations.SearchStrategy.EXHAUSTIVE)
.stream(TestPropertySource.class)
.map(TestPropertySourceUtils::makeTestPropertySourceAttribute)
.collect(Collectors.toList());
}

AnnotationDescriptor<TestPropertySource> descriptor = findAnnotationDescriptor(testClass, annotationType);
Assert.notNull(descriptor, String.format(
"Could not find an 'annotation declaring class' for annotation type [%s] and class [%s]",
annotationType.getName(), testClass.getName()));
private static TestPropertySourceAttributes makeTestPropertySourceAttribute(
MergedAnnotation<TestPropertySource> annotation) {

while (descriptor != null) {
TestPropertySource testPropertySource = descriptor.synthesizeAnnotation();
Class<?> rootDeclaringClass = descriptor.getRootDeclaringClass();
if (logger.isTraceEnabled()) {
logger.trace(String.format("Retrieved @TestPropertySource [%s] for declaring class [%s].",
TestPropertySource testPropertySource = annotation.synthesize();
Class<?> rootDeclaringClass = (Class<?>) annotation.getSource();
if (logger.isTraceEnabled()) {
logger.trace(String.format(
"Retrieved @TestPropertySource [%s] for declaring class [%s].",
testPropertySource, rootDeclaringClass.getName()));
}
TestPropertySourceAttributes attributes =
new TestPropertySourceAttributes(rootDeclaringClass, testPropertySource);
if (logger.isTraceEnabled()) {
logger.trace("Resolved TestPropertySource attributes: " + attributes);
}
attributesList.add(attributes);
descriptor = findAnnotationDescriptor(rootDeclaringClass.getSuperclass(), annotationType);
}

return attributesList;
TestPropertySourceAttributes attributes = new TestPropertySourceAttributes(
rootDeclaringClass, testPropertySource);
if (logger.isTraceEnabled()) {
logger.trace("Resolved TestPropertySource attributes: " + attributes);
}
return attributes;
}

private static String[] mergeLocations(List<TestPropertySourceAttributes> attributesList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2019 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.env.repeatable;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.test.context.TestPropertySource;

/**
* A custom annotation with properties defined by the {@link TestPropertySource}.
*
* @author Anatoliy Korovin
* @since 5.2
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@TestPropertySource(properties = "meta = value from meta-annotation")
public @interface AnnotationWithTestProperty {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2019 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.env.repeatable;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.test.context.TestPropertySource;

/**
* A custom annotation which defined properties file in the {@link TestPropertySource}.
*
* @author Anatoliy Korovin
* @since 5.2
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@TestPropertySource("meta.properties")
public @interface AnnotationWithTestPropertyInPropertiesFile {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2002-2019 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.env.repeatable;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.test.context.TestPropertySource;

/**
* A custom annotation with foo property defined by the {@link TestPropertySource}.
*
* @author Anatoliy Korovin
* @since 5.2
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@TestPropertySource(properties = "foo = value from meta-annotation")
public @interface FooTestProperty {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2002-2019 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.env.repeatable;

import org.springframework.test.context.TestPropertySource;

/**
* Abstract parent class with foo property definition for tests.
*
* @author Anatoliy Korovin
* @since 5.2
*/
@TestPropertySource(properties = "foo = value from parent class")
public abstract class FooTestPropertyDeclaration {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2002-2019 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.env.repeatable;

import org.springframework.test.context.TestPropertySource;

/**
* Abstract parent class with multiple properties definition for tests.
*
* @author Anatoliy Korovin
* @since 5.2
*/
@TestPropertySource(properties = "first = value from parent class")
@TestPropertySource(properties = "second = value from parent class")
public abstract class ParentClassWithMultipleTestProperties {
}
Loading