使用测试属性源进行上下文配置
Spring 框架对具有属性源层次结构的环境概念提供了首要支持,您可以使用特定于测试的属性源配置集成测试。与在@Configuration
类上使用的@PropertySource
注解相反,您可以在测试类上声明@TestPropertySource
注解以声明测试属性文件的资源位置或内联属性。这些测试属性源将添加到为带注解的集成测试加载的ApplicationContext
的Environment
中的PropertySources
集合中。
您可以将
|
声明测试属性源
您可以使用@TestPropertySource
的locations
或value
属性配置测试属性文件。
默认情况下,支持传统的和基于 XML 的java.util.Properties
文件格式——例如,"classpath:/com/example/test.properties"
或"file:///path/to/file.xml"
。从 Spring Framework 6.1 开始,您可以通过@TestPropertySource
中的factory
属性配置自定义PropertySourceFactory
,以便支持不同的文件格式,例如 JSON、YAML 等。
每个路径都被解释为 Spring Resource
。普通路径(例如,"test.properties"
)被视为相对于定义测试类的包的类路径资源。以斜杠开头的路径被视为绝对类路径资源(例如:"/org/example/test.xml"
)。使用指定的资源协议加载引用 URL 的路径(例如,以classpath:
、file:
或http:
为前缀的路径)。
路径中的属性占位符(例如${…}
)将针对Environment
进行解析。
从 Spring Framework 6.1 开始,也支持资源位置模式——例如,"classpath*:/config/*.properties"
。
以下示例使用测试属性文件
-
Java
-
Kotlin
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
// class body...
}
1 | 指定具有绝对路径的属性文件。 |
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
// class body...
}
1 | 指定具有绝对路径的属性文件。 |
您可以通过使用@TestPropertySource
的properties
属性以键值对的形式配置内联属性,如下一个示例所示。所有键值对都作为单个测试PropertySource
添加到封闭的Environment
中,并具有最高的优先级。
键值对支持的语法与 Java 属性文件中条目的语法相同
-
key=value
-
key:value
-
key value
虽然可以使用上述任何语法变体和键和值之间任意数量的空格来定义属性,但建议您在一个测试套件中使用一种语法变体和一致的空格——例如,考虑始终使用 原因是您提供的精确字符串将用于确定上下文缓存的键。因此,为了从上下文缓存中获益,您必须确保始终如一地定义内联属性。 |
以下示例设置两个内联属性
-
Java
-
Kotlin
@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port = 4242"}) (1)
class MyIntegrationTests {
// class body...
}
1 | 通过字符串数组设置两个属性。 |
@ContextConfiguration
@TestPropertySource(properties = ["timezone = GMT", "port = 4242"]) (1)
class MyIntegrationTests {
// class body...
}
1 | 通过字符串数组设置两个属性。 |
从 Spring Framework 6.1 开始,您可以使用文本块在一个String
中定义多个内联属性。以下示例使用文本块设置两个内联属性
-
Java
-
Kotlin
@ContextConfiguration
@TestPropertySource(properties = """
timezone = GMT
port = 4242
""") (1)
class MyIntegrationTests {
// class body...
}
1 | 通过文本块设置两个属性。 |
@ContextConfiguration
@TestPropertySource(properties = ["""
timezone = GMT
port = 4242
"""]) (1)
class MyIntegrationTests {
// class body...
}
1 | 通过文本块设置两个属性。 |
这意味着您可以在单个测试类上声明多个 此外,您可以在测试类上声明多个组合注解,这些注解都使用 直接存在的 |
默认属性文件检测
如果@TestPropertySource
声明为一个空注解(即,没有为locations
或properties
属性显式赋值),则会尝试检测相对于声明该注解的类的默认属性文件。例如,如果带注解的测试类是com.example.MyTest
,则相应的默认属性文件是classpath:com/example/MyTest.properties
。如果无法检测到默认属性文件,则会抛出IllegalStateException
异常。
优先级
测试属性的优先级高于操作系统环境中定义的属性、Java系统属性或应用程序使用@PropertySource
声明式或以编程方式添加的属性源。因此,测试属性可用于有选择地覆盖从系统和应用程序属性源加载的属性。此外,内联属性的优先级高于从资源位置加载的属性。但是,请注意,通过@DynamicPropertySource
注册的属性优先级高于通过@TestPropertySource
加载的属性。
在下面的示例中,timezone
和port
属性以及在"/test.properties"
中定义的任何属性都将覆盖在系统和应用程序属性源中定义的相同名称的任何属性。此外,如果"/test.properties"
文件定义了timezone
和port
属性的条目,则这些条目将被使用properties
属性声明的内联属性覆盖。以下示例演示如何同时在文件和内联中指定属性。
-
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...
}
继承和覆盖测试属性源
@TestPropertySource
支持布尔型inheritLocations
和inheritProperties
属性,它们表示是否应继承超类声明的属性文件的资源位置和内联属性。这两个标志的默认值均为true
。这意味着测试类继承任何超类声明的位置和内联属性。具体来说,测试类的位置和内联属性将附加到超类声明的位置和内联属性。因此,子类可以选择扩展位置和内联属性。请注意,后面出现的属性会隐藏(即覆盖)前面出现的相同名称的属性。此外,上述优先级规则也适用于继承的测试属性源。
如果@TestPropertySource
中的inheritLocations
或inheritProperties
属性设置为false
,则测试类的相应位置或内联属性将隐藏并有效地替换超类定义的配置。
测试配置也可以从封闭类继承。有关详细信息,请参阅@Nested 测试类配置。 |
在下面的示例中,BaseTest
的ApplicationContext
仅使用base.properties
文件作为测试属性源来加载。相反,ExtendedTest
的ApplicationContext
使用base.properties
和extended.properties
文件作为测试属性源位置来加载。以下示例演示如何使用properties
文件在子类及其超类中定义属性。
-
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() {
// ...
}
在下面的示例中,BaseTest
的ApplicationContext
仅使用内联key1
属性来加载。相反,ExtendedTest
的ApplicationContext
使用内联key1
和key2
属性来加载。以下示例演示如何使用内联属性在子类及其超类中定义属性。
-
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() {
// ...
}