上下文层级结构

在编写依赖于已加载的 Infra ApplicationContext 的集成测试时,针对单个上下文进行测试通常就足够了。但是,有时针对 ApplicationContext 实例的层级结构进行测试是有益的甚至必要的。例如,如果你正在开发 Web MVC web 应用程序,你通常有一个由 Infra ContextLoaderListener 加载的根 WebApplicationContext 和一个由 Infra MockDispatcherHandler 加载的子 WebApplicationContext。这导致了一个父子上下文层级结构,其中共享组件和基础设施配置在根上下文中声明,并在子上下文中由特定于 web 的组件使用。另一个用例可以在 Infra Batch 应用程序中找到,你通常有一个父上下文为共享批处理基础设施提供配置,以及一个子上下文用于特定批处理作业的配置。

你可以通过在单个测试类上或测试类层次结构中声明带有 @ContextHierarchy 注解的上下文配置来编写使用上下文层级结构的集成测试。如果在测试类层次结构中的多个类上声明了上下文层级结构,你还可以合并或覆盖上下文层级结构中特定命名级别的上下文配置。当合并层级结构中给定级别的配置时,配置资源类型(即 XML 配置文件或组件类)必须一致。否则,完全可以接受在上下文层级结构中使用不同的资源类型配置不同的级别。

本节中其余基于 JUnit Jupiter 的示例展示了需要使用上下文层级结构的集成测试的常见配置场景。

具有上下文层级结构的单个测试类

ControllerIntegrationTests 代表了 Web MVC web 应用程序的典型集成测试场景,它通过声明一个包含两个级别的上下文层级结构来实现,一级用于根 WebApplicationContext(使用 TestAppConfig @Configuration 类加载),另一级用于 dispatcher Handler WebApplicationContext(使用 WebConfig @Configuration 类加载)。自动装配到测试实例中的 WebApplicationContext 是子上下文(即层级结构中最低的上下文)的上下文。以下清单显示了此配置场景:

@ExtendWith(InfraExtension.class)
@WebAppConfiguration
@ContextHierarchy({
  @ContextConfiguration(classes = TestAppConfig.class),
  @ContextConfiguration(classes = WebConfig.class)
})
class ControllerIntegrationTests {

  @Autowired
  WebApplicationContext wac;

  // ...
}

具有隐式父上下文的类层次结构

此示例中的测试类在测试类层次结构中定义了上下文层级结构。AbstractWebTests 声明了 Infra 驱动的 web 应用程序中根 WebApplicationContext 的配置。但请注意,AbstractWebTests 没有声明 @ContextHierarchy。因此,AbstractWebTests 的子类可以选择参与上下文层级结构或遵循 @ContextConfiguration 的标准语义。SoapWebServiceTestsRestWebServiceTests 都扩展了 AbstractWebTests 并使用 @ContextHierarchy 定义了上下文层级结构。结果是加载了三个应用程序上下文(每个 @ContextConfiguration 声明一个),并且基于 AbstractWebTests 中的配置加载的应用程序上下文被设置为为具体子类加载的每个上下文的父上下文。以下清单显示了此配置场景:

@ExtendWith(InfraExtension.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 {}

具有合并上下文层级结构配置的类层次结构

此示例中的类展示了使用命名层级结构级别来合并上下文层级结构中特定级别的配置。BaseTests 在层级结构中定义了两个级别:parentchildExtendedTests 扩展了 BaseTests 并指示 Infra TestContext 框架合并 child 层级结构级别的上下文配置,方法是确保 @ContextConfiguration 中的 name 属性中声明的名称都是 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

@ExtendWith(InfraExtension.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 {}

具有覆盖上下文层级结构配置的类层次结构

与前面的示例相比,此示例演示了如何通过将 @ContextConfiguration 中的 inheritLocations 标志设置为 false 来覆盖上下文层级结构中给定命名级别的配置。因此,ExtendedTests 的应用程序上下文仅从 /test-user-config.xml 加载,并且其父级设置为从 /app-config.xml 加载的上下文。以下清单显示了此配置场景:

  • Java

@ExtendWith(InfraExtension.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 {}
在上下文层级结构中弄脏上下文
如果你在上下文配置为上下文层级结构一部分的测试中使用 @DirtiesContext,你可以使用 hierarchyMode 标志来控制如何清除上下文缓存。有关更多详细信息,请参阅 Infra 测试注解 中关于 @DirtiesContext 的讨论以及 @DirtiesContext javadoc。