Context Configuration with Test Property Sources

Spring Framework 一流地支持具有属性源层次结构的环境概念,并且您可以配置包含特定于测试的属性源的集成测试。与 @Configuration 类中使用的 @PropertySource 注释相反,你可以在测试类中声明 @TestPropertySource 注释,以声明测试属性文件或内联属性的资源位置。这些测试属性源被添加到为带注释的集成测试加载的 ApplicationContextEnvironment 中的 PropertySources 集中。

The Spring Framework has first-class support for the notion of an environment with a hierarchy of property sources, and you can configure integration tests with test-specific property sources. In contrast to the @PropertySource annotation used on @Configuration classes, you can declare the @TestPropertySource annotation on a test class to declare resource locations for test properties files or inlined properties. These test property sources are added to the set of PropertySources in the Environment for the ApplicationContext loaded for the annotated integration test.

你可以在任何 SmartContextLoader SPI 的实现中使用 @TestPropertySource,但 @TestPropertySource 不受较旧的 ContextLoader SPI 实现支持。

You can use @TestPropertySource with any implementation of the SmartContextLoader SPI, but @TestPropertySource is not supported with implementations of the older ContextLoader SPI.

SmartContextLoader 的实现能通过 MergedContextConfiguration 中的 getPropertySourceDescriptors()getPropertySourceProperties() 方法访问合并的测试属性源值。

Implementations of SmartContextLoader gain access to merged test property source values through the getPropertySourceDescriptors() and getPropertySourceProperties() methods in MergedContextConfiguration.

Declaring Test Property Sources

你可以使用 @TestPropertySourcelocationsvalue 属性配置测试属性文件。

You can configure test properties files by using the locations or value attribute of @TestPropertySource.

默认情况下,支持传统的和基于 XML 的 java.util.Properties 文件格式,例如 "classpath:/com/example/test.properties""file:///path/to/file.xml"。从 Spring Framework 6.1 开始,你可以通过 @TestPropertySource 中的 factory 属性配置自定义 PropertySourceFactory,以支持不同的文件格式,例如 JSON、YAML 等。

By default, both traditional and XML-based java.util.Properties file formats are supported — for example, "classpath:/com/example/test.properties" or "file:///path/to/file.xml". As of Spring Framework 6.1, you can configure a custom PropertySourceFactory via the factory attribute in @TestPropertySource in order to support a different file format such as JSON, YAML, etc.

每条路径都被解释为 Spring Resource。普通路径(例如 "test.properties")被视为相对于定义测试类的包的类路径资源。以斜杠开头的路径被视为绝对类路径资源(例如:"/org/example/test.xml")。引用 URL 的路径(例如以 classpath:, file:, 或 http: 为前缀的路径)使用指定的资源协议加载。

Each path is interpreted as a Spring Resource. A plain path (for example, "test.properties") is treated as a classpath resource that is relative to the package in which the test class is defined. A path starting with a slash is treated as an absolute classpath resource (for example: "/org/example/test.xml"). A path that references a URL (for example, a path prefixed with classpath:, file:, or http:) is loaded by using the specified resource protocol.

路径中的属性占位符(例如 ${…​}) 将针对 Environment 进行解析。

Property placeholders in paths (such as ${…​}) will be resolved against the Environment.

从 Spring Framework 6.1 开始,也支持资源位置模式 — 例如,"classpath*:/config/*.properties"

As of Spring Framework 6.1, resource location patterns are also supported — for example, "classpath*:/config/*.properties".

以下示例使用测试属性文件:

The following example uses a test properties file:

Java
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
	// class body...
}
1 Specifying a properties file with an absolute path.
Kotlin
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
	// class body...
}
2 Specifying a properties file with an absolute path.

你可以使用 @TestPropertySourceproperties 属性以键值对的形式配置内联属性,如下一示例所示。所有键值对作为具有最高优先级的单个测试 PropertySource 添加到包含的 Environment 中。

You can configure inlined properties in the form of key-value pairs by using the properties attribute of @TestPropertySource, as shown in the next example. All key-value pairs are added to the enclosing Environment as a single test PropertySource with the highest precedence.

键值对支持的语法与 Java 属性文件中为条目定义的语法相同:

The supported syntax for key-value pairs is the same as the syntax defined for entries in a Java properties file:

  • key=value

  • key:value

  • key value

虽然可以使用上面任何一种语法变体和在键和值之间的任何空格来定义属性,但建议您在测试套件中使用一种语法变体和一致的间距——例如,始终考虑使用 key = value 而不是 key= value、`key=value`等。同样,如果您使用文本块定义内联属性,则应在整个测试套件中始终使用文本块进行内联属性。

Although properties can be defined using any of the above syntax variants and any number of spaces between the key and the value, it is recommended that you use one syntax variant and consistent spacing within your test suite — for example, consider always using key = value instead of key= value, key=value, etc. Similarly, if you define inlined properties using text blocks you should consistently use text blocks for inlined properties throughout your test suite.

原因在于您提供的 exact 字符串将用于确定情境缓存的键。因此,要从情境缓存中受益,您必须确保一致地定义内联属性。

The reason is that the exact strings you provide will be used to determine the key for the context cache. Consequently, to benefit from the context cache you must ensure that you define inlined properties consistently.

以下示例设置了两个内联属性:

The following example sets two inlined properties:

Java
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port = 4242"}) (1)
class MyIntegrationTests {
	// class body...
}
1 Setting two properties via an array of strings.
Kotlin
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port = 4242"]) (1)
class MyIntegrationTests {
	// class body...
}
2 Setting two properties via an array of strings.

从 Spring 框架 6.1 开始,您可以使用 文本块 在单个 String 中定义多个内联属性。以下示例使用文本块设置了两个内联属性:

As of Spring Framework 6.1, you can use text blocks to define multiple inlined properties in a single String. The following example sets two inlined properties using a text block:

Java
@ContextConfiguration
@TestPropertySource(properties = """
	timezone = GMT
	port = 4242
	""") (1)
class MyIntegrationTests {
	// class body...
}
1 Setting two properties via a text block.
Kotlin
@ContextConfiguration
@TestPropertySource(properties = ["""
	timezone = GMT
	port = 4242
	"""]) (1)
class MyIntegrationTests {
	// class body...
}
2 Setting two properties via a text block.

从 Spring 框架 5.2 开始,@TestPropertySource 可用作 可重复注释 。这意味着您可以在一个测试类上多次声明 @TestPropertySource,此后 @TestPropertySource 注释中的 locationsproperties 将覆盖前一个 @TestPropertySource 注释中的 locationsproperties

As of Spring Framework 5.2, @TestPropertySource can be used as repeatable annotation. That means that you can have multiple declarations of @TestPropertySource on a single test class, with the locations and properties from later @TestPropertySource annotations overriding those from previous @TestPropertySource annotations.

此外,您可以在测试类上声明多个组合注释,每个注释都使用 @TestPropertySource 进行元注释,所有这些 @TestPropertySource 声明都将应用于您的测试属性源。

In addition, you may declare multiple composed annotations on a test class that are each meta-annotated with @TestPropertySource, and all of those @TestPropertySource declarations will contribute to your test property sources.

直接呈现 @TestPropertySource 注释始终优先于元呈现 @TestPropertySource 注释。换句话说,直接呈现 @TestPropertySource 注释中的 locationsproperties 将覆盖用作元注释的 @TestPropertySource 注释中的 locationsproperties

Directly present @TestPropertySource annotations always take precedence over meta-present @TestPropertySource annotations. In other words, locations and properties from a directly present @TestPropertySource annotation will override the locations and properties from a @TestPropertySource annotation used as a meta-annotation.

Default Properties File Detection

如果 @TestPropertySource 被声明为一个空注释(即没有 locationsproperties 属性的显式值),则会尝试检测声明该注释的类相对于默认属性文件的检测。例如,如果带注释的测试类是 com.example.MyTest,则相应的默认属性文件是 classpath:com/example/MyTest.properties。如果无法检测到默认值,则会抛出 IllegalStateException

If @TestPropertySource is declared as an empty annotation (that is, without explicit values for the locations or properties attributes), an attempt is made to detect a default properties file relative to the class that declared the annotation. For example, if the annotated test class is com.example.MyTest, the corresponding default properties file is classpath:com/example/MyTest.properties. If the default cannot be detected, an IllegalStateException is thrown.

Precedence

测试属性的优先级高于操作系统环境、Java 系统属性或应用程序使用 @PropertySource 声明式或以编程的方式(通过 @TestPropertySource)添加的属性源中定义的那些属性。因此,测试属性可以用来选择性地覆盖从系统和应用程序属性源加载的属性。此外,内联属性的优先级高于从资源位置加载的属性。但是请注意,通过 @DynamicPropertySource 注册的属性的优先级高于通过 @TestPropertySource 加载的那些属性。

Test properties have higher precedence than those defined in the operating system’s environment, Java system properties, or property sources added by the application declaratively by using @PropertySource or programmatically. Thus, test properties can be used to selectively override properties loaded from system and application property sources. Furthermore, inlined properties have higher precedence than properties loaded from resource locations. Note, however, that properties registered via @DynamicPropertySource have higher precedence than those loaded via @TestPropertySource.

在下一个示例中,timezoneport 属性以及在“/test.properties”中定义的任何属性都将覆盖在系统和应用程序属性源中定义的名称相同的任何属性。此外,如果 "/test.properties" 文件为 timezoneport 属性定义了条目,则会被使用 properties 属性声明的内联属性覆盖。以下示例显示了如何在文件和内联中指定属性:

In the next example, the timezone and port properties and any properties defined in "/test.properties" override any properties of the same name that are defined in system and application property sources. Furthermore, if the "/test.properties" file defines entries for the timezone and port properties those are overridden by the inlined properties declared by using the properties attribute. The following example shows how to specify properties both in a file and inline:

  • Java

  • Kotlin

@ContextConfiguration
@TestPropertySource(
	locations = "/test.properties",
	properties = {"timezone = GMT", "port = 4242"}
)
class MyIntegrationTests {
	// class body...
}
@ContextConfiguration
@TestPropertySource("/test.properties",
		properties = ["timezone = GMT", "port = 4242"]
)
class MyIntegrationTests {
	// class body...
}

Inheriting and Overriding Test Property Sources

@TestPropertySource 支持布尔型 inheritLocationsinheritProperties 属性,它们表示是否应继承超类声明的属性文件和内联属性的资源位置。这两个标志的默认值为 true。这意味着测试类会继承超类声明的位置和内联属性。具体来说,测试类的位置和内联属性将附加到超类声明的位置和内联属性后面。因此,子类可以选择扩展位置和内联属性。请注意,稍后出现的属性会覆盖(即覆盖)先前的名称相同的属性。此外,上述优先级规则也适用于继承的测试属性源。

@TestPropertySource supports boolean inheritLocations and inheritProperties attributes that denote whether resource locations for properties files and inlined properties declared by superclasses should be inherited. The default value for both flags is true. This means that a test class inherits the locations and inlined properties declared by any superclasses. Specifically, the locations and inlined properties for a test class are appended to the locations and inlined properties declared by superclasses. Thus, subclasses have the option of extending the locations and inlined properties. Note that properties that appear later shadow (that is, override) properties of the same name that appear earlier. In addition, the aforementioned precedence rules apply for inherited test property sources as well.

如果 @TestPropertySource 中的 inheritLocationsinheritProperties 属性设置为 false,则测试类的位置或内联属性将覆盖并有效地替换超类定义的配置。

If the inheritLocations or inheritProperties attribute in @TestPropertySource is set to false, the locations or inlined properties, respectively, for the test class shadow and effectively replace the configuration defined by superclasses.

从 Spring Framework 5.3 开始,还可以从封闭类继承测试配置。请参阅 @Nested test class configuration 了解详情。

As of Spring Framework 5.3, test configuration may also be inherited from enclosing classes. See @Nested test class configuration for details.

在下一个示例中,BaseTestApplicationContext 只能使用 base.properties 文件作为测试属性源进行加载。相反,ExtendedTestApplicationContext 会使用 base.propertiesextended.properties 文件作为测试属性源位置进行加载。以下示例显示了如何使用 properties 文件在子类及其超类中定义属性:

In the next example, the ApplicationContext for BaseTest is loaded by using only the base.properties file as a test property source. In contrast, the ApplicationContext for ExtendedTest is loaded by using the base.properties and extended.properties files as test property source locations. The following example shows how to define properties in both a subclass and its superclass by using properties files:

  • Java

  • Kotlin

@TestPropertySource("base.properties")
@ContextConfiguration
class BaseTest {
	// ...
}

@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
	// ...
}
@TestPropertySource("base.properties")
@ContextConfiguration
open class BaseTest {
	// ...
}

@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest : BaseTest() {
	// ...
}

在下一个示例中,BaseTestApplicationContext 只能使用内联 key1 属性进行加载。相反,ExtendedTestApplicationContext 会使用内联 key1key2 属性进行加载。以下示例显示了如何使用内联属性在子类及其超类中定义属性:

In the next example, the ApplicationContext for BaseTest is loaded by using only the inlined key1 property. In contrast, the ApplicationContext for ExtendedTest is loaded by using the inlined key1 and key2 properties. The following example shows how to define properties in both a subclass and its superclass by using inline properties:

  • Java

  • Kotlin

@TestPropertySource(properties = "key1 = value1")
@ContextConfiguration
class BaseTest {
	// ...
}

@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
	// ...
}
@TestPropertySource(properties = ["key1 = value1"])
@ContextConfiguration
open class BaseTest {
	// ...
}

@TestPropertySource(properties = ["key2 = value2"])
@ContextConfiguration
class ExtendedTest : BaseTest() {
	// ...
}