上下文管理
每个TestContext都为其所负责的测试实例提供上下文管理和缓存支持。测试实例不会自动获得对配置好的ApplicationContext的访问权限。但是,如果一个测试类实现了ApplicationContextAware接口,那么该测试实例就会获得对ApplicationContext的引用。需要注意的是,AbstractJUnit4SpringContextTests和AbstractTestNGSpringContextTests实现了ApplicationContextAware接口,因此能够自动地访问ApplicationContext。
@Autowired ApplicationContext
作为一种替代实现ApplicationContextAware接口的方法,你可以通过在字段或setter方法上使用@Autowired注解来注入应用程序上下文,如下例所示:
- Java
- Kotlin
@SpringJUnitConfig
class MyTest {
@Autowired 1
ApplicationContext applicationContext;
// 类体内容...
}
注入
ApplicationContext。
@SpringJUnitConfig
class MyTest {
@Autowired 1
lateinit var applicationContext: ApplicationContext
// 类体内容...
}
注入
ApplicationContext。
同样地,如果你的测试配置为加载WebApplicationContext,你可以如下将Web应用程序上下文注入到测试中:
- Java
- Kotlin
@SpringJUnitWebConfig 1
class MyWebAppTest {
@Autowired 2
WebApplicationContext wac;
// 类体内容...
}
配置
WebApplicationContext。注入
WebApplicationContext。
@SpringJUnitWebConfig 1
class MyWebAppTest {
@Autowired 2
lateinit var wac: WebApplicationContext
// 类体内容...
}
配置
WebApplicationContext。注入
WebApplicationContext。
使用@Autowired进行依赖注入是由DependencyInjectionTestExecutionListener提供的,该监听器是默认配置好的(详见测试 fixture 的依赖注入)。
使用TestContext框架的测试类不需要扩展任何特定类或实现特定接口来配置它们的应用程序上下文。相反,通过在类级别声明@ContextConfiguration注解来实现配置。如果你的测试类没有明确声明应用程序上下文资源的位置或组件类,那么已配置的ContextLoader将决定如何从默认位置或默认配置类中加载上下文。除了上下文资源的位置和组件类之外,还可以通过应用程序上下文初始化器来配置应用程序上下文。
以下部分将解释如何使用Spring的@ContextConfiguration注解来配置测试ApplicationContext,可以通过XML配置文件、Groovy脚本、组件类(通常是@Configuration类)或上下文初始化器来实现。另外,对于更高级的用例,你还可以实现并配置自己的自定义SmartContextLoader。
部分总结
📄️ 使用XML资源进行上下文配置
要使用XML配置文件为测试加载ApplicationContext,请在测试类上添加@ContextConfiguration注解,并将locations属性配置为一个数组,该数组包含XML配置元数据的资源位置。纯路径或相对路径(例如,context.xml)被视为相对于定义测试类的包的类路径资源。以斜杠开头的路径被视为绝对类路径位置(例如,/org/example/config.xml)。表示资源URL的路径(即,以classpath:、file:、http:等为前缀的路径)将按原样使用。
📄️ 使用Groovy脚本进行上下文配置
要使用Groovy脚本(这些脚本利用了Groovy Bean Definition DSL)为测试加载ApplicationContext,可以在测试类上添加@ContextConfiguration注解,并通过一个数组来配置locations或value属性,该数组中包含了Groovy脚本的资源路径。Groovy脚本的资源查找逻辑与XML配置文件所描述的逻辑是相同的。
📄️ 使用组件类进行上下文配置
要使用组件类来为测试加载ApplicationContext(请参见基于Java的容器配置),您可以使用@ContextConfiguration注解标注您的测试类,并将classes属性配置为一个包含组件类引用的数组。以下示例展示了如何进行操作:
📄️ 混合使用XML、Groovy脚本和组件类
有时,为了配置测试环境中的ApplicationContext,可能需要将XML配置文件、Groovy脚本以及组件类(通常是@Configuration注解的类)混合使用。例如,如果在生产环境中使用XML配置,那么在测试环境中你可能会决定使用@Configuration注解的类来配置特定的Spring管理的组件,反之亦然。
📄️ 使用上下文定制器进行上下文配置
ContextCustomizer负责在bean定义被加载到上下文中之后、但在上下文被刷新之前,对提供的ConfigurableApplicationContext进行自定义。
📄️ 使用上下文初始化器进行上下文配置
要使用上下文初始化器为测试配置 ApplicationContext,需在测试类上添加 @ContextConfiguration 注解,并将 initializers 属性配置为一个数组,该数组包含实现 ApplicationContextInitializer 接口的类的引用。然后,这些声明的上下文初始化器将用于初始化为测试加载的 ConfigurableApplicationContext。请注意,每个声明的初始化器所支持的具体的 ConfigurableApplicationContext 类型必须与 SmartContextLoader 所创建的 ApplicationContext 的类型兼容(通常是 GenericApplicationContext)。此外,初始化器的调用顺序取决于它们是否实现了 Spring 的 Ordered 接口,或者是否被 Spring 的 @Order 注解或标准的 @Priority 注解所标记。以下示例展示了如何使用初始化器:
📄️ 上下文配置继承
@ContextConfiguration 支持 boolean 类型的 inheritLocations 和 inheritInitializers 属性,这些属性表示是否应继承由超类声明的资源位置、组件类以及上下文初始化器。这两个属性的默认值都是 true。这意味着测试类会继承任何超类声明的资源位置、组件类以及上下文初始化器。具体来说,测试类的资源位置或组件类会被添加到超类声明的资源位置或带注解的类列表中;同样,给定测试类的初始化器也会被添加到测试超类定义的初始化器集合中。因此,子类可以选择扩展资源位置、组件类或上下文初始化器。
📄️ 使用环境配置文件进行上下文配置
Spring框架对环境(environment)和配置文件(profile,也称为“bean定义配置文件”)的概念提供了一流的支持,可以配置集成测试以便在各种测试场景下激活特定的bean定义配置文件。这是通过使用@ActiveProfiles注解来标注测试类,并在加载测试的ApplicationContext时提供应激活的配置文件列表来实现的。
📄️ 使用测试属性源进行上下文配置
Spring框架对具有属性源层次结构的环境概念提供了了一流的支持,你可以配置使用特定于测试的属性源来进行集成测试。与用在@Configuration类上的@PropertySource注解不同,你可以在测试类上声明@TestPropertySource注解,以指定测试属性文件或内联属性的资源位置。这些测试属性源会被添加到为被注解的集成测试加载的ApplicationContext中的PropertySources集合中。
📄️ 使用动态属性源进行上下文配置
Spring TestContext框架通过DynamicPropertyRegistry、@DynamicPropertySource注解以及DynamicPropertyRegistrar API提供了对动态属性的支持。
📄️ 加载WebApplicationContext
要指示TestContext框架加载WebApplicationContext而不是标准的ApplicationContext,你可以在相应的测试类上添加@WebAppConfiguration注解。
📄️ 使用Web Mocks
为了提供全面的Web测试支持,TestContext框架默认启用了一个名为ServletTestExecutionListener的组件。在对WebApplicationContext进行测试时,该组件会在每个测试方法执行前使用Spring Web的RequestContextHolder来设置线程局部状态(thread-local state),并根据通过@WebAppConfiguration配置的基资源路径(base resource path)创建MockHttpServletRequest、MockHttpServletResponse以及ServletWebRequest对象。ServletTestExecutionListener还确保这些MockHttpServletResponse和ServletWebRequest能够被注入到测试实例中;测试完成后,它会清理这些线程局部状态。
📄️ 上下文缓存
一旦TestContext框架为某个测试加载了一个ApplicationContext(或WebApplicationContext),那么该上下文就会被缓存起来,并在同一个测试套件中后续的所有声明了相同唯一上下文配置的测试中重复使用。要理解缓存的工作原理,重要的是要明白“唯一”和“测试套件”这两个概念的含义。
📄️ 上下文暂停
从Spring Framework 7.0开始,当存储在上下文缓存(参见“上下文缓存”)中的ApplicationContext不再被主动使用时,该ApplicationContext可以被暂停,下次从缓存中检索该上下文时则会自动重新启动。具体来说,重新启动会触发应用程序上下文中所有自动启动的bean,从而有效恢复这些bean的生命周期状态。这确保了在测试不使用该上下文期间,上下文中的后台进程不会处于活动运行状态。例如,JMS监听器容器、定时任务以及实现Lifecycle或SmartLifecycle接口的任何其他组件,在测试再次使用该上下文之前都会处于“停止”状态。不过需要注意的是,SmartLifecycle组件可以通过在SmartLifecycle#isPauseable()方法中返回false来选择不参与暂停过程。
📄️ 上下文失败阈值
从Spring Framework 6.1开始,引入了一种上下文失败阈值策略,该策略有助于避免重复尝试加载失败的ApplicationContext。默认情况下,失败阈值被设置为1,这意味着对于给定的上下文缓存键(参见“上下文缓存”),只会进行一次尝试来加载ApplicationContext。任何后续尝试加载同一上下文缓存键的ApplicationContext都会立即引发 IllegalStateException,并附带一条错误信息,说明该尝试被预先跳过了。这种行为通过避免重复加载永远无法成功加载的ApplicationContext(例如,由于配置错误或缺少阻止当前环境加载上下文的外部资源),使得单个测试类和测试套件能够更快地失败。
📄️ 上下文层次结构
在编写依赖于已加载的Spring ApplicationContext的集成测试时,通常只需针对单个ApplicationContext进行测试即可。然而,在某些情况下,针对ApplicationContext实例的层次结构进行测试会更有益,甚至是必要的。例如,如果你正在开发一个Spring MVC Web应用程序,通常会有一个由Spring的ContextLoaderListener加载的根WebApplicationContext,以及一个由Spring的DispatcherServlet加载的子WebApplicationContext。这就形成了一个父子上下文层次结构:共享组件和基础设施配置在根上下文中声明,而特定于Web的组件则在子上下文中使用这些配置。另一个应用场景出现在Spring Batch应用程序中,这种情况下通常会有一个父上下文来提供共享批处理基础设施的配置,还有一个子上下文用于特定批处理作业的配置。