对于最新稳定版本,请使用 Spring Framework 7.0.6spring-doc.cadn.net.cn

使用测试属性源进行上下文配置

Spring 框架对具有属性源层次结构的环境概念提供了一流的支持,您可以使用特定于测试的属性源来配置集成测试。与在 @PropertySource 类上使用的 @Configuration 注解不同,您可以在测试类上声明 @TestPropertySource 注解,以指定测试属性文件的资源位置或内联属性。这些测试属性源会被添加到为带注解的集成测试所加载的 PropertySourcesEnvironment 中的 ApplicationContext 集合里。spring-doc.cadn.net.cn

您可以将 @TestPropertySource 与任何 SmartContextLoader SPI 的实现一起使用,但 @TestPropertySource 不支持与旧版 ContextLoader SPI 的实现一起使用。spring-doc.cadn.net.cn

SmartContextLoader 的实现类通过 getPropertySourceLocations() 中的 getPropertySourceProperties()MergedContextConfiguration 方法来访问合并后的测试属性源值。spring-doc.cadn.net.cn

声明测试属性源

你可以通过使用 locations 注解的 value@TestPropertySource 属性来配置测试属性文件。spring-doc.cadn.net.cn

同时支持传统的和基于 XML 的属性文件格式——例如, "classpath:/com/example/test.properties""file:///path/to/file.xml"spring-doc.cadn.net.cn

每个路径都会被解释为一个 Spring Resource。普通路径(例如,"test.properties")被视为相对于定义测试类的包的类路径资源。以斜杠开头的路径被视为绝对类路径资源(例如:"/org/example/test.xml")。引用 URL 的路径(例如,以前缀 classpath:file:http: 开头的路径)将使用指定的资源协议进行加载。不允许使用资源位置通配符(如 **/*.properties):每个位置必须恰好解析为一个 .properties.xml 资源。spring-doc.cadn.net.cn

以下示例使用了一个测试属性文件:spring-doc.cadn.net.cn

@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
	// class body...
}
1 使用绝对路径指定属性文件。
@ContextConfiguration
@TestPropertySource("/test.properties") (1)
class MyIntegrationTests {
	// class body...
}
1 使用绝对路径指定属性文件。

你可以通过使用 properties 注解的 @TestPropertySource 属性,以键值对的形式配置内联属性,如下一个示例所示。所有键值对都会作为单个测试 Environment 添加到外围的 PropertySource 中,并具有最高优先级。spring-doc.cadn.net.cn

支持的键值对语法与 Java 属性文件中定义的条目语法相同:spring-doc.cadn.net.cn

以下示例设置了两个内联属性:spring-doc.cadn.net.cn

@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 5.2 开始,@TestPropertySource 可用作可重复注解。 这意味着你可以在同一个测试类上声明多个 @TestPropertySource, 其中后面声明的 locations 注解中的 properties@TestPropertySource 会覆盖前面声明的 @TestPropertySource 注解中的相应内容。spring-doc.cadn.net.cn

此外,你可以在测试类上声明多个组合注解,每个组合注解都使用 @TestPropertySource 进行元注解,所有这些 @TestPropertySource 声明都会贡献到你的测试属性源中。spring-doc.cadn.net.cn

直接声明的 @TestPropertySource 注解始终优先于作为元注解(meta-annotation)使用的 @TestPropertySource 注解。换句话说,直接声明的 locations 注解中的 properties@TestPropertySource 将覆盖作为元注解使用的 locations 注解中的 properties@TestPropertySourcespring-doc.cadn.net.cn

默认属性文件检测

如果 @TestPropertySource 被声明为一个空注解(即未显式指定 locationsproperties 属性的值),则会尝试在声明该注解的类所在位置相对路径下查找默认的属性文件。例如,如果被注解的测试类是 com.example.MyTest,则对应的默认属性文件为 classpath:com/example/MyTest.properties。如果无法检测到默认属性文件,则会抛出 IllegalStateException 异常。spring-doc.cadn.net.cn

优先级

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

在下一个示例中,timezoneport 属性以及在 "/test.properties" 中定义的任何属性,将覆盖系统和应用程序属性源中同名的属性。此外,如果 "/test.properties" 文件中定义了 timezoneport 属性的条目,则这些条目会被通过 properties 属性声明的内联属性所覆盖。以下示例展示了如何同时在文件和内联方式中指定属性:spring-doc.cadn.net.cn

@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 支持布尔类型的 inheritLocationsinheritProperties 属性,用于指示是否应继承超类中声明的属性文件资源位置和内联属性。这两个标志的默认值均为 true。这意味着测试类会继承其所有超类所声明的位置和内联属性。具体而言,测试类的位置和内联属性会被追加到超类声明的位置和内联属性之后。因此,子类可以选择性地扩展这些位置和内联属性。请注意,后出现的属性会遮蔽(即覆盖)先出现的同名属性。此外,上述优先级规则同样适用于继承而来的测试属性源。spring-doc.cadn.net.cn

如果 inheritLocations 注解中的 inheritProperties@TestPropertySource 属性被设置为 false,则该测试类的资源位置或内联属性将分别覆盖并有效替换其父类中定义的配置。spring-doc.cadn.net.cn

自 Spring Framework 5.3 起,测试配置也可以从外部类继承。有关详细信息,请参阅 @Nested 测试类配置

在下一个示例中,ApplicationContextBaseTest 仅使用 base.properties 文件作为测试属性源进行加载。相比之下,ApplicationContextExtendedTest 则使用 base.propertiesextended.properties 两个文件作为测试属性源位置进行加载。以下示例展示了如何通过 properties 文件在子类及其父类中定义属性:spring-doc.cadn.net.cn

@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() {
	// ...
}

在下一个示例中,ApplicationContextBaseTest 仅使用内联的 key1 属性加载。相比之下,ApplicationContextExtendedTest 则使用内联的 key1key2 属性进行加载。以下示例展示了如何在子类及其父类中使用内联属性来定义属性:spring-doc.cadn.net.cn

@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() {
	// ...
}