Support custom properties file formats in @TestPropertySource
Spring Framework 4.3 introduced the `PropertySourceFactory` SPI for use
with `@PropertySource` on `@Configuration` classes; however, prior to
this commit there was no mechanism to support custom properties file
formats in `@TestPropertySource` for integration tests.
This commit introduces support for configuring a custom
`PropertySourceFactory` via a new `factory` attribute in
`@TestPropertySource` in order to support custom file formats such as
JSON, YAML, etc.
For example, if you create a YamlPropertySourceFactory, you can use it
in integration tests as follows.
@SpringJUnitConfig
@TestPropertySource(locations = "/test.yaml", factory = YamlPropertySourceFactory.class)
class MyTestClass { /* ... /* }
If a custom factory is not specified, traditional `*.properties` and
`*.xml` based `java.util.Properties` file formats are supported, which
was the existing behavior.
Closes gh-30981
This commit is contained in:
@@ -18,6 +18,7 @@ package org.springframework.test.context;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -409,9 +410,9 @@ class MergedContextConfigurationTests {
|
||||
void equalsWithSameContextCustomizers() {
|
||||
Set<ContextCustomizer> customizers = Collections.singleton(mock());
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers, loader, null, null);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, List.of(), null, customizers, loader, null, null);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers, loader, null, null);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, List.of(), null, customizers, loader, null, null);
|
||||
assertThat(mergedConfig2).isEqualTo(mergedConfig1);
|
||||
}
|
||||
|
||||
@@ -424,9 +425,9 @@ class MergedContextConfigurationTests {
|
||||
Set<ContextCustomizer> customizers2 = Collections.singleton(mock());
|
||||
|
||||
MergedContextConfiguration mergedConfig1 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers1, loader, null, null);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, List.of(), null, customizers1, loader, null, null);
|
||||
MergedContextConfiguration mergedConfig2 = new MergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, null, null, customizers2, loader, null, null);
|
||||
EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, List.of(), null, customizers2, loader, null, null);
|
||||
assertThat(mergedConfig2).isNotEqualTo(mergedConfig1);
|
||||
assertThat(mergedConfig1).isNotEqualTo(mergedConfig2);
|
||||
}
|
||||
|
||||
62
spring-test/src/test/java/org/springframework/test/context/env/YamlPropertySourceFactory.java
vendored
Normal file
62
spring-test/src/test/java/org/springframework/test/context/env/YamlPropertySourceFactory.java
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||
import org.springframework.core.env.PropertiesPropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.EncodedResource;
|
||||
import org.springframework.core.io.support.PropertySourceFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Demo {@link PropertySourceFactory} that provides YAML support.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
class YamlPropertySourceFactory implements PropertySourceFactory {
|
||||
|
||||
@Override
|
||||
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource) {
|
||||
Resource resource = encodedResource.getResource();
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = getNameForResource(resource);
|
||||
}
|
||||
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
|
||||
factoryBean.setResources(resource);
|
||||
factoryBean.afterPropertiesSet();
|
||||
Properties properties = factoryBean.getObject();
|
||||
return new PropertiesPropertySource(name, properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the description for the given Resource; if the description is
|
||||
* empty, return the class name of the resource plus its identity hash code.
|
||||
*/
|
||||
private static String getNameForResource(Resource resource) {
|
||||
String name = resource.getDescription();
|
||||
if (!StringUtils.hasText(name)) {
|
||||
name = resource.getClass().getSimpleName() + "@" + System.identityHashCode(resource);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
||||
39
spring-test/src/test/java/org/springframework/test/context/env/YamlTestProperties.java
vendored
Normal file
39
spring-test/src/test/java/org/springframework/test/context/env/YamlTestProperties.java
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
/**
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@TestPropertySource(factory = YamlPropertySourceFactory.class)
|
||||
public @interface YamlTestProperties {
|
||||
|
||||
@AliasFor(annotation = TestPropertySource.class)
|
||||
String[] value();
|
||||
|
||||
}
|
||||
59
spring-test/src/test/java/org/springframework/test/context/env/YamlTestPropertySourceTests.java
vendored
Normal file
59
spring-test/src/test/java/org/springframework/test/context/env/YamlTestPropertySourceTests.java
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.support.PropertySourceFactory;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TestPropertySource @TestPropertySource} support
|
||||
* with a custom YAML {@link PropertySourceFactory}.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
@YamlTestProperties("test-properties.yaml")
|
||||
class YamlTestPropertySourceTests {
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(delimiterString = "->", textBlock = """
|
||||
environments.dev.url -> https://dev.example.com
|
||||
environments.dev.name -> 'Developer Setup'
|
||||
environments.prod.url -> https://prod.example.com
|
||||
environments.prod.name -> 'My Cool App'
|
||||
""")
|
||||
void propertyIsAvailableInEnvironment(String property, String value, @Autowired ConfigurableEnvironment env) {
|
||||
assertThat(env.getProperty(property)).isEqualTo(value);
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
/* no user beans required for these tests */
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -16,15 +16,9 @@
|
||||
|
||||
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.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.env.repeatable.LocalPropertiesFileAndMetaPropertiesFileTests.MetaFileTestProperty;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link TestPropertySource @TestPropertySource} as a
|
||||
@@ -48,15 +42,4 @@ class LocalPropertiesFileAndMetaPropertiesFileTests extends AbstractRepeatableTe
|
||||
assertEnvironmentValue("key2", "meta file");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Composed annotation that declares a properties file via
|
||||
* {@link TestPropertySource @TestPropertySource}.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@TestPropertySource("meta.properties")
|
||||
@interface MetaFileTestProperty {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.test.context.env.YamlTestProperties;
|
||||
|
||||
/**
|
||||
* Analogous to {@link LocalPropertiesFileAndMetaPropertiesFileTests} except
|
||||
* that the local file is YAML.
|
||||
*
|
||||
* @author Sam Brannen
|
||||
* @since 6.1
|
||||
*/
|
||||
@YamlTestProperties("local.yaml")
|
||||
@MetaFileTestProperty
|
||||
class LocalYamlFileAndMetaPropertiesFileTests extends AbstractRepeatableTestPropertySourceTests {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
assertEnvironmentValue("key1", "local file");
|
||||
assertEnvironmentValue("key2", "meta file");
|
||||
|
||||
assertEnvironmentValue("environments.dev.url", "https://dev.example.com");
|
||||
assertEnvironmentValue("environments.dev.name", "Developer Setup");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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;
|
||||
|
||||
/**
|
||||
* Composed annotation that declares a properties file via
|
||||
* {@link TestPropertySource @TestPropertySource}.
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@TestPropertySource("meta.properties")
|
||||
@interface MetaFileTestProperty {
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -187,6 +187,7 @@ class BootstrapTestUtilsMergedConfigTests extends AbstractContextConfigurationUt
|
||||
assertMergedConfigForLocationPaths(RelativeFooXmlLocation.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void assertMergedConfigForLocationPaths(Class<?> testClass) {
|
||||
MergedContextConfiguration mergedConfig = buildMergedContextConfiguration(testClass);
|
||||
|
||||
|
||||
@@ -18,7 +18,9 @@ package org.springframework.test.context.support;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -29,6 +31,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PropertySourceDescriptor;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.mock.env.MockPropertySource;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
@@ -295,7 +298,9 @@ class TestPropertySourceUtilsTests {
|
||||
MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass);
|
||||
SoftAssertions.assertSoftly(softly -> {
|
||||
softly.assertThat(mergedPropertySources).isNotNull();
|
||||
softly.assertThat(mergedPropertySources.getLocations()).isEqualTo(expectedLocations);
|
||||
Stream<String> locations = mergedPropertySources.getPropertySourceDescriptors().stream()
|
||||
.map(PropertySourceDescriptor::locations).flatMap(List::stream);
|
||||
softly.assertThat(locations).containsExactly(expectedLocations);
|
||||
softly.assertThat(mergedPropertySources.getProperties()).isEqualTo(expectedProperties);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -33,14 +33,15 @@ class AnnotationConfigWebContextLoaderTests {
|
||||
|
||||
|
||||
@Test
|
||||
void configMustNotContainLocations() throws Exception {
|
||||
@SuppressWarnings("deprecation")
|
||||
void configMustNotContainLocations() {
|
||||
AnnotationConfigWebContextLoader loader = new AnnotationConfigWebContextLoader();
|
||||
WebMergedContextConfiguration mergedConfig = new WebMergedContextConfiguration(getClass(),
|
||||
new String[] { "config.xml" }, EMPTY_CLASS_ARRAY, null, EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY,
|
||||
EMPTY_STRING_ARRAY, "resource/path", loader, null, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> loader.loadContext(mergedConfig))
|
||||
.withMessageContaining("does not support resource locations");
|
||||
.isThrownBy(() -> loader.loadContext(mergedConfig))
|
||||
.withMessageContaining("does not support resource locations");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -34,12 +34,13 @@ class GenericXmlWebContextLoaderTests {
|
||||
@Test
|
||||
void configMustNotContainAnnotatedClasses() throws Exception {
|
||||
GenericXmlWebContextLoader loader = new GenericXmlWebContextLoader();
|
||||
@SuppressWarnings("deprecation")
|
||||
WebMergedContextConfiguration mergedConfig = new WebMergedContextConfiguration(getClass(), EMPTY_STRING_ARRAY,
|
||||
new Class<?>[] { getClass() }, null, EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY, EMPTY_STRING_ARRAY,
|
||||
"resource/path", loader, null, null);
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(() -> loader.loadContext(mergedConfig))
|
||||
.withMessageContaining("does not support annotated classes");
|
||||
.isThrownBy(() -> loader.loadContext(mergedConfig))
|
||||
.withMessageContaining("does not support annotated classes");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user