Introduce @TestPropertySource support in the TCF

Spring Framework 3.1 introduced an Environment abstraction with support
for hierarchical PropertySources that can be configured
programmatically as well as declaratively via the @PropertySource
annotation. However, prior to this commit, there was no way to
declaratively configure PropertySources in integration tests in the
Spring TestContext Framework (TCF).

This commit introduces declarative support for PropertySources in the
TCF via a new class-level @TestPropertySource annotation. This
annotation provides two options for declaring test property sources:

 - The 'locations' attribute allows developers to declare external
   resource locations for test properties files.

 - The 'properties' attribute allows developers to declare inlined
   properties in the form of key-value pairs.

Test properties files are added to the Environment before all other
property sources and can therefore override system and application
property sources. Similarly, inlined properties are added to the
Environment before all other property sources and can therefore
override system property sources, application property sources, and
test properties files.

Specifically, this commit introduces the following major changes:

 - Introduced @TestPropertySource annotation along with internal
   TestPropertySourceAttributes, MergedTestPropertySources, and
   TestPropertySourceUtils for working with test property sources
   within the TCF.

 - All TestContextBootstrappers have been modified to support the
   merged property resource locations and inlined properties from
   @TestPropertySource.

 - MergedContextConfiguration (and consequently the context caching
   key) is now additionally based on the merged property resource
   locations and inlined properties from @TestPropertySource. The same
   applies to WebMergedContextConfiguration.

 - AbstractContextLoader's prepareContext() method now adds
   PropertySources for all resource locations and inlined properties
   from the supplied MergedContextConfiguration to the Environment of
   the supplied ApplicationContext. All subclasses of
   AbstractGenericContextLoader and AbstractGenericWebContextLoader
   therefore automatically provide support for @TestPropertySource.

Issue: SPR-12051
This commit is contained in:
Sam Brannen
2014-08-01 14:33:11 +03:00
parent 6665634675
commit 2cf4147ba8
30 changed files with 1690 additions and 130 deletions

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an explicitly named properties file that overrides a
* application-level property configured via
* {@link PropertySource @PropertySource} on an
* {@link Configuration @Configuration} class.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource("ApplicationPropertyOverridePropertiesFileTestPropertySourceTests.properties")
public class ApplicationPropertyOverridePropertiesFileTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("test override", env.getProperty("explicit"));
}
// -------------------------------------------------------------------
@Configuration
@PropertySource("classpath:/org/springframework/test/context/env/explicit.properties")
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests that verify detection of a default properties file
* when {@link TestPropertySource @TestPropertySource} is <em>empty</em>.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource
public class DefaultPropertiesFileDetectionTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
// from DefaultPropertiesFileDetectionTestPropertySourceTests.properties
assertEnvironmentValue("riddle", "auto detected");
}
protected void assertEnvironmentValue(String key, String expected) {
assertEquals("Value of key [" + key + "].", expected, env.getProperty(key));
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an explicitly named properties file.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource("explicit.properties")
public class ExplicitPropertiesFileTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
String userHomeKey = "user.home";
assertEquals(System.getProperty(userHomeKey), env.getProperty(userHomeKey));
assertEquals("enigma", env.getProperty("explicit"));
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
/**
* Integration tests that verify detection of default properties files
* when {@link TestPropertySource @TestPropertySource} is <em>empty</em>
* at multiple levels within a class hierarchy.
*
* @author Sam Brannen
* @since 4.1
*/
@TestPropertySource
public class ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests extends
DefaultPropertiesFileDetectionTestPropertySourceTests {
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
super.verifyPropertiesAreAvailableInEnvironment();
// from ExtendedDefaultPropertiesFileDetectionTestPropertySourceTests.properties
assertEnvironmentValue("enigma", "auto detected");
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.springframework.test.context.TestPropertySource;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an inherited explicitly named properties file that is
* referenced using a relative path.
*
* @author Sam Brannen
* @since 4.1
*/
public class InheritedRelativePathPropertiesFileTestPropertySourceTests extends
ExplicitPropertiesFileTestPropertySourceTests {
/* all tests are in superclass */
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an inlined properties.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource(properties = { "foo = bar", "enigma: 42" })
public class InlinedPropertiesTestPropertySourceTests {
@Autowired
protected Environment env;
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("bar", env.getProperty("foo"));
assertEquals(42, env.getProperty("enigma", Integer.class).intValue());
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.*;
/**
* Integration tests that verify support for overriding properties from
* properties files via inlined properties configured with
* {@link TestPropertySource @TestPropertySource}.
*
* @author Sam Brannen
* @since 4.1
*/
@TestPropertySource(properties = { "explicit = inlined", "extended = inlined1", "extended = inlined2" })
public class MergedPropertiesFilesOverriddenByInlinedPropertiesTestPropertySourceTests extends
MergedPropertiesFilesTestPropertySourceTests {
@Test
@Override
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("inlined", env.getProperty("explicit"));
}
@Test
@Override
public void verifyExtendedPropertiesAreAvailableInEnvironment() {
assertEquals("inlined2", env.getProperty("extended"));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import static org.junit.Assert.*;
/**
* Integration tests that verify support for contributing additional properties
* files to the Spring {@code Environment} via {@link TestPropertySource @TestPropertySource}.
*
* @author Sam Brannen
* @since 4.1
*/
@TestPropertySource("extended.properties")
public class MergedPropertiesFilesTestPropertySourceTests extends
ExplicitPropertiesFileTestPropertySourceTests {
@Test
public void verifyExtendedPropertiesAreAvailableInEnvironment() {
assertEquals(42, env.getProperty("extended", Integer.class).intValue());
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2002-2014 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.test.context.env;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an explicitly named properties file that overrides a
* system property.
*
* @author Sam Brannen
* @since 4.1
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestPropertySource("SystemPropertyOverridePropertiesFileTestPropertySourceTests.properties")
public class SystemPropertyOverridePropertiesFileTestPropertySourceTests {
private static final String KEY = SystemPropertyOverridePropertiesFileTestPropertySourceTests.class.getSimpleName() + ".riddle";
@Autowired
protected Environment env;
@BeforeClass
public static void setSystemProperty() {
System.setProperty(KEY, "override me!");
}
@AfterClass
public static void removeSystemProperty() {
System.setProperty(KEY, "");
}
@Test
public void verifyPropertiesAreAvailableInEnvironment() {
assertEquals("enigma", env.getProperty(KEY));
}
// -------------------------------------------------------------------
@Configuration
static class Config {
/* no user beans required for these tests */
}
}

View File

@@ -0,0 +1 @@
SystemPropertyOverridePropertiesFileTestPropertySourceTests.riddle = enigma

View File

@@ -0,0 +1 @@
explicit = enigma

View File

@@ -0,0 +1 @@
extended = 42

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2002-2014 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.test.context.env.subpackage;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.env.ExplicitPropertiesFileTestPropertySourceTests;
/**
* Integration tests for {@link TestPropertySource @TestPropertySource}
* support with an inherited explicitly named properties file that is
* referenced using a relative path within a parent package.
*
* @author Sam Brannen
* @since 4.1
*/
public class SubpackageInheritedRelativePathPropertiesFileTestPropertySourceTests extends
ExplicitPropertiesFileTestPropertySourceTests {
/* all tests are in superclass */
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2002-2014 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.test.context.support;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.test.context.TestPropertySource;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.test.context.support.TestPropertySourceUtils.*;
/**
* Unit tests for {@link TestPropertySourceUtils}.
*
* @author Sam Brannen
* @since 4.1
*/
public class TestPropertySourceUtilsTests {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
@Rule
public ExpectedException expectedException = ExpectedException.none();
private void assertMergedTestPropertySources(Class<?> testClass, String[] expectedLocations,
String[] expectedProperties) {
MergedTestPropertySources mergedPropertySources = buildMergedTestPropertySources(testClass);
assertNotNull(mergedPropertySources);
assertArrayEquals(expectedLocations, mergedPropertySources.getLocations());
assertArrayEquals(expectedProperties, mergedPropertySources.getProperties());
}
@Test
public void emptyAnnotation() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(startsWith("Could not detect default properties file for test"));
expectedException.expectMessage(containsString("EmptyPropertySources.properties"));
buildMergedTestPropertySources(EmptyPropertySources.class);
}
@Test
public void extendedEmptyAnnotation() {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(startsWith("Could not detect default properties file for test"));
expectedException.expectMessage(containsString("ExtendedEmptyPropertySources.properties"));
buildMergedTestPropertySources(ExtendedEmptyPropertySources.class);
}
@Test
public void value() {
assertMergedTestPropertySources(ValuePropertySources.class, new String[] { "classpath:/value.xml" },
EMPTY_STRING_ARRAY);
}
@Test
public void locationsAndValueAttributes() {
expectedException.expect(IllegalStateException.class);
buildMergedTestPropertySources(LocationsAndValuePropertySources.class);
}
@Test
public void locationsAndProperties() {
assertMergedTestPropertySources(LocationsAndPropertiesPropertySources.class, new String[] {
"classpath:/foo1.xml", "classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
}
@Test
public void inheritedLocationsAndProperties() {
assertMergedTestPropertySources(InheritedPropertySources.class, new String[] { "classpath:/foo1.xml",
"classpath:/foo2.xml" }, new String[] { "k1a=v1a", "k1b: v1b" });
}
@Test
public void extendedLocationsAndProperties() {
assertMergedTestPropertySources(ExtendedPropertySources.class, new String[] { "classpath:/foo1.xml",
"classpath:/foo2.xml", "classpath:/bar1.xml", "classpath:/bar2.xml" }, new String[] { "k1a=v1a",
"k1b: v1b", "k2a v2a", "k2b: v2b" });
}
@Test
public void overriddenLocations() {
assertMergedTestPropertySources(OverriddenLocationsPropertySources.class,
new String[] { "classpath:/baz.properties" }, new String[] { "k1a=v1a", "k1b: v1b", "key = value" });
}
@Test
public void overriddenProperties() {
assertMergedTestPropertySources(OverriddenPropertiesPropertySources.class, new String[] {
"classpath:/foo1.xml", "classpath:/foo2.xml", "classpath:/baz.properties" }, new String[] { "key = value" });
}
@Test
public void overriddenLocationsAndProperties() {
assertMergedTestPropertySources(OverriddenLocationsAndPropertiesPropertySources.class,
new String[] { "classpath:/baz.properties" }, new String[] { "key = value" });
}
// -------------------------------------------------------------------
@TestPropertySource
static class EmptyPropertySources {
}
@TestPropertySource
static class ExtendedEmptyPropertySources extends EmptyPropertySources {
}
@TestPropertySource(locations = "/foo", value = "/bar")
static class LocationsAndValuePropertySources {
}
@TestPropertySource("/value.xml")
static class ValuePropertySources {
}
@TestPropertySource(locations = { "/foo1.xml", "/foo2.xml" }, properties = { "k1a=v1a", "k1b: v1b" })
static class LocationsAndPropertiesPropertySources {
}
static class InheritedPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = { "/bar1.xml", "/bar2.xml" }, properties = { "k2a v2a", "k2b: v2b" })
static class ExtendedPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritLocations = false)
static class OverriddenLocationsPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritProperties = false)
static class OverriddenPropertiesPropertySources extends LocationsAndPropertiesPropertySources {
}
@TestPropertySource(locations = "/baz.properties", properties = "key = value", inheritLocations = false, inheritProperties = false)
static class OverriddenLocationsAndPropertiesPropertySources extends LocationsAndPropertiesPropertySources {
}
}

View File

@@ -38,6 +38,7 @@ public class AnnotationConfigWebContextLoaderTests {
@Test
@SuppressWarnings("deprecation")
public void configMustNotContainLocations() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(containsString("does not support resource locations"));

View File

@@ -37,6 +37,7 @@ public class GenericXmlWebContextLoaderTests {
@Test
@SuppressWarnings("deprecation")
public void configMustNotContainAnnotatedClasses() throws Exception {
expectedException.expect(IllegalStateException.class);
expectedException.expectMessage(containsString("does not support annotated classes"));