|
对于最新稳定版本,请使用 Spring Framework 7.0.6! |
上下文层次结构
在编写依赖于已加载的 Spring ApplicationContext 的集成测试时,通常针对单个上下文进行测试就足够了。然而,有时针对 ApplicationContext 实例的层次结构进行测试会更有益,甚至必不可少。例如,如果您正在开发一个 Spring MVC Web 应用程序,通常会有一个由 Spring 的 WebApplicationContext 加载的根 ContextLoaderListener,以及一个由 Spring 的 WebApplicationContext 加载的子 DispatcherServlet。这形成了一种父子上下文层次结构:共享组件和基础设施配置声明在根上下文中,并由子上下文中的 Web 特定组件使用。另一个使用场景出现在 Spring Batch 应用程序中,通常会有一个父上下文提供共享批处理基础设施的配置,以及一个子上下文用于特定批处理作业的配置。
你可以通过在单个测试类或测试类层次结构中使用 @ContextHierarchy 注解声明上下文配置,来编写使用上下文层次结构的集成测试。如果在测试类层次结构中的多个类上声明了上下文层次结构,你还可以合并或覆盖上下文层次结构中特定命名层级的上下文配置。在合并层次结构中某一层级的配置时,配置资源类型(即 XML 配置文件或组件类)必须保持一致。不过,在上下文层次结构的不同层级使用不同的资源类型进行配置是完全允许的。
本节中其余基于 JUnit Jupiter 的示例展示了需要使用上下文层次结构的集成测试的常见配置场景。
具有上下文层次结构的单个测试类
ControllerIntegrationTests 通过声明一个包含两个层级的上下文层次结构,代表了 Spring MVC Web 应用程序的一种典型集成测试场景:
一个用于根 WebApplicationContext(使用 TestAppConfig @Configuration 类加载),
另一个用于 Dispatcher Servlet 的 WebApplicationContext(使用 WebConfig @Configuration 类加载)。
自动装配到测试实例中的 WebApplicationContext 是子上下文的(即层次结构中最底层的上下文)。
以下代码清单展示了该配置场景:
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextHierarchy({
@ContextConfiguration(classes = TestAppConfig.class),
@ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {
@Autowired
WebApplicationContext wac;
// ...
}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextHierarchy(
ContextConfiguration(classes = [TestAppConfig::class]),
ContextConfiguration(classes = [WebConfig::class]))
class ControllerIntegrationTests {
@Autowired
lateinit var wac: WebApplicationContext
// ...
}
具有隐式父上下文的类层次结构
本示例中的测试类在测试类层次结构中定义了一个上下文层次结构。AbstractWebTests 声明了由 Spring 支持的 Web 应用中根WebApplicationContext的配置。但请注意,AbstractWebTests并未声明@ContextHierarchy。因此,AbstractWebTests的子类可以选择参与上下文层次结构,或遵循@ContextConfiguration的标准语义。SoapWebServiceTests和RestWebServiceTests均扩展自AbstractWebTests,并通过使用@ContextHierarchy定义了一个上下文层次结构。结果是加载了三个应用上下文(每个@ContextConfiguration声明对应一个),并且基于AbstractWebTests中配置加载的应用上下文被设置为各个具体子类所加载上下文的父上下文。以下清单展示了此配置场景:
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
public abstract class AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/soap-ws-config.xml"))
public class SoapWebServiceTests extends AbstractWebTests {}
@ContextHierarchy(@ContextConfiguration("/spring/rest-ws-config.xml"))
public class RestWebServiceTests extends AbstractWebTests {}
@ExtendWith(SpringExtension::class)
@WebAppConfiguration
@ContextConfiguration("file:src/main/webapp/WEB-INF/applicationContext.xml")
abstract class AbstractWebTests
@ContextHierarchy(ContextConfiguration("/spring/soap-ws-config.xml"))
class SoapWebServiceTests : AbstractWebTests()
@ContextHierarchy(ContextConfiguration("/spring/rest-ws-config.xml"))
class RestWebServiceTests : AbstractWebTests()
带有合并上下文层次结构配置的类层次结构
本示例中的类展示了如何使用命名的层次结构级别,以便在上下文层次结构中合并特定级别的配置。BaseTests 定义了层次结构中的两个级别:parent 和 child。ExtendedTests 扩展了 BaseTests,并指示 Spring TestContext Framework 通过确保 @ContextConfiguration 中 name 属性所声明的名称均为 child,来合并 child 层次结构级别的上下文配置。结果是加载了三个应用程序上下文:一个用于 /app-config.xml,一个用于 /user-config.xml,另一个用于 {"/user-config.xml", "/order-config.xml"}。与前一个示例一样,从 /app-config.xml 加载的应用程序上下文被设置为从 /user-config.xml 和 {"/user-config.xml", "/order-config.xml"} 加载的上下文的父上下文。
以下列表展示了此配置场景:
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(name = "child", locations = "/order-config.xml")
)
class ExtendedTests extends BaseTests {}
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
ContextConfiguration(name = "child", locations = ["/order-config.xml"])
)
class ExtendedTests : BaseTests() {}
具有覆盖上下文层次结构配置的类层次结构
与前面的示例不同,本示例演示了如何通过将 inheritLocations 中的 @ContextConfiguration 标志设置为 false,来覆盖上下文层次结构中指定命名层级的配置。因此,ExtendedTests 的应用上下文仅从 /test-user-config.xml 加载,并将其父上下文设置为从 /app-config.xml 加载的上下文。以下代码清单展示了该配置场景:
-
Java
-
Kotlin
@ExtendWith(SpringExtension.class)
@ContextHierarchy({
@ContextConfiguration(name = "parent", locations = "/app-config.xml"),
@ContextConfiguration(name = "child", locations = "/user-config.xml")
})
class BaseTests {}
@ContextHierarchy(
@ContextConfiguration(
name = "child",
locations = "/test-user-config.xml",
inheritLocations = false
))
class ExtendedTests extends BaseTests {}
@ExtendWith(SpringExtension::class)
@ContextHierarchy(
ContextConfiguration(name = "parent", locations = ["/app-config.xml"]),
ContextConfiguration(name = "child", locations = ["/user-config.xml"]))
open class BaseTests {}
@ContextHierarchy(
ContextConfiguration(
name = "child",
locations = ["/test-user-config.xml"],
inheritLocations = false
))
class ExtendedTests : BaseTests() {}
在上下文层次结构中使上下文失效
如果在测试中使用 @DirtiesContext,且该测试的上下文被配置为上下文层次结构的一部分,则可以使用 hierarchyMode 标志来控制如何清除上下文缓存。有关更多详细信息,请参阅 Spring 测试注解 中关于 @DirtiesContext 的讨论,以及 @DirtiesContext 的 Javadoc。 |